--- url: /guide/project-configuration/add-packages.md --- # Add Packages {#add-packages} ## Introduction Xmake has built-in support for package dependency integration. You can declare the required dependency packages through the [add\_requires](/api/description/global-interfaces#add-requires) interface. Then, through the [add\_packages](/api/description/project-target#add-packages) interface, bind the declared package to the required compilation target, for example: ```lua [xmake.lua] add_requires("tbox 1.6.*", "libpng ~1.16", "zlib") target("foo") set_kind("binary") add_files("src/*.c") add_packages("tbox", "libpng") target("bar") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` Among them, `add_requires` is a global interface, used for package configuration declaration, and Xmake will trigger search and installation based on the declared package. Since a project may have multiple target programs, each target program may require different dependency packages, so we also need to bind the target through `add_packages`. In the above configuration example, the foo target binds the tbox and libpng packages, while the bar target binds the zlib package. ## Basic Usage and Common Scenarios {#basic-usage} * `add_requires("pkgname")` declares dependencies, supports version, optional, alias, etc. * `add_packages("pkgname")` binds packages to targets, automatically adds links, includedirs, etc. ### Typical Scenarios * Multiple targets depend on different packages * One target depends on multiple packages * Supports C/C++/Fortran/multi-platform ## API Details {#api-details} ### Specify Package Version ```lua add_requires("tbox 1.6.*", "libpng ~1.16", "zlib") ``` ### Optional Package ```lua add_requires("foo", {optional = true}) ``` ### Disable System Library ```lua add_requires("foo", {system = false}) ``` ### Specify Alias ```lua add_requires("foo", {alias = "myfoo"}) add_packages("myfoo") ``` ### Platform/Arch Limitation ```lua add_requires("foo", {plat = "windows", arch = "x64"}) ``` ### Pass Package Configs ```lua add_requires("tbox", {configs = {small = true}}) ``` ### Pass Configs to Dependencies ```lua add_requireconfs("spdlog.fmt", {configs = {header_only = true}}) ``` ## Advanced Features {#advanced-features} * Semantic version, branch, commit support * Debug/release package support * Multi-repo, private repo support * Local/system/remote package priority * Extra build arguments ## Package Instance APIs {#package-instance} Available in custom rules, after\_install, etc.: * `package:name()` get package name * `package:version_str()` get version * `package:installdir()` get install dir * `package:get("links")` get link libraries * `package:get("includedirs")` get include dirs ## Typical Examples {#examples} ### 1. Optional Dependency ```lua add_requires("foo", {optional = true}) target("bar") add_packages("foo") ``` ### 2. Depend on Branch/Commit ```lua add_requires("tbox master") add_requires("zlib 1.2.11") ``` ### 3. Pass Configs to Package ```lua add_requires("spdlog", {configs = {header_only = true}}) ``` ### 4. Depend on Local Package 1. Create a local package repository directory in your project (e.g. `local-repo/packages/foo/xmake.lua`). 2. Add the local repository in `xmake.lua`: ```lua add_repositories("myrepo local-repo") add_requires("foo") ``` 3. Local package structure example: ``` local-repo/ packages/ foo/ xmake.lua ``` 4. Now you can use the local package just like an official one via `add_requires("foo")`. ## Best Practices {#best-practices} 1. Use `add_requires` + `add_packages` for declaration and binding 2. Use `{optional = true}` for optional packages 3. Use `{plat=..., arch=...}` for precise control on multi-platform 4. Use `add_requireconfs` for recursive dependency config 5. Use `xmake require --info pkg` to query package parameters ## More Information {#more-information} * Official package usage: [Using Official Packages](/guide/package-management/using-official-packages) * Package dependency API: [Package Dependency API](/api/description/package-dependencies) * Package instance APIs: [package instance API](/api/scripts/package-instance) * Package management and search: You can also use the [xrepo CLI tool](/guide/package-management/xrepo-cli) to search, install, and uninstall packages. --- --- url: /posts/add-package-and-autocheck.md --- xmake encapsulates dependency libraries, dependency headers, dependency types, and dependency interfaces uniformly using the option mechanism, and further introduces a package mechanism at a higher level, making adding and detecting dependencies more modular and simpler. Let's look at how xmake's package mechanism works through a specific example. Suppose your project already has two packages: `zlib.pkg` and `polarssl.pkg` (how to build packages will be explained in detail later; for now, you can refer to the examples of existing packages in [TBOX dependencies](https://github.com/waruqi/tbox/tree/master/pkg)). Your project directory structure is as follows: ``` demo - xmake.lua - src main.c - pkg zlib.pkg polarssl.pkg ``` You can modify `xmake.lua` to use the two dependency packages above: ```lua -- Add dependency package directory, all required packages will be searched from this directory add_packagedirs("pkg") -- Add target target("demo") -- Set program type to binary executable set_kind("binary") -- Add source files add_files("src/*.c") -- Add polarssl and zlib packages through option mechanism, if detection passes, they will be automatically linked -- The first time you run xmake config or xmake build, it will automatically detect them and cache the configuration -- To re-detect, you can run xmake config -c to clear the original configuration and reconfigure everything add_options("polarssl", "zlib") -- Set auto-generated configuration header file, if mysql detection passes, it will generate CONFIG_PACKAGE_HAVE_MYSQL switch set_config_h("$(buildir)/config.h") -- Set config.h macro switch prefix: CONFIG_xxxx set_config_h_prefix("CONFIG") -- Add header file search directory, here to search for config.h add_includedirs("$(buildir)") ``` Next, here's how to use it in your code: ```c #include // Include auto-generated config.h header file // Search path is set in ./build directory #include "config.h" // If zlib exists on current platform, use it #ifdef CONFIG_PACKAGE_HAVE_ZLIB # include "zlib/zlib.h" #endif // If polarssl exists on current platform, use it #ifdef CONFIG_PACKAGE_HAVE_POLARSSL # include "polarssl/polarssl.h" #endif int main(int argc, char** argv) { printf("hello world!\n"); return 0; } ``` The above is the simplest example of package usage. Now let's see how this `zlib.pkg` is generated: If this package is developed by your own project `xxx`, you only need to run `xmake p` to package it, and it will automatically generate an `xxx.pkg` package in the `./build` directory, which you can directly use in other projects. If it's a third-party library, you need to build it yourself, but it's also very convenient. If you can't, you can refer to some packages in the existing [TBOX dependencies](https://github.com/waruqi/tbox/tree/master/pkg) and modify them. A pkg package directory structure: ``` zlib.pkg - inc (header file directory, optional) - zlib/zlib.h - lib (link library directory, optional) - linux/i386/libz.a - windows/i386/zlib.lib - xmake.lua (package description file) ``` Among them, `inc` and `lib` are optional. The specific logic is described in `xmake.lua`. The default package logic generated by xmake will first detect whether there are available libraries and header files in the `zlib.pkg` directory for the current platform. If the detection fails, it will then detect the system platform. Of course, you can also modify the detection logic yourself. You don't have to do it this way. You only need to describe the `xxx.pkg/xmake.lua` file according to your needs. Let's look at the description logic of `zlib.pkg/xmake.lua` I provided here: ```lua -- Add a zlib package auto-configuration option option("zlib") -- Set whether to display in xmake f -h configuration menu -- If you want your package to allow users to manually disable it in the project, then enable this set_showmenu(true) -- Display related description information in xmake f -h set_description("The mysql package") -- If detection passes, define macro switch to config.h add_defines_h_if_ok("$(prefix)_PACKAGE_HAVE_ZLIB") -- Detect linking add_links("z") -- Add detected link library directory, here set to first detect if link library exists in zlib.pkg/lib/ for related platform, then detect system -- If this is not set, xmake can only detect link libraries in some system directories, such as: /usr/lib, /usr/local/lib -- If it cannot be detected in common system directories, but you have installed this library, you can set the search directory for detection yourself add_linkdirs("lib/$(plat)/$(arch)") -- Detect if #include "zlib/zlib.h" can compile successfully add_cincludes("zlib/zlib.h") -- Add some detected header file directories, by default it will search in zlib.pkg/inc, of course you can also specify other directories add_includedirs("inc/$(plat)", "inc") ``` As long as you describe `xxx.pkg/xmake.lua` well, a package can be used by xmake and automatically detected. It uses xmake's option mechanism. Of course, in the package, you can not only detect dependency libraries and header files, but also detect whether certain required interfaces, type definitions, etc. exist. And the detection mechanism completely uses lua syntax, supports if conditional logic, you can do some special processing for some specific platforms, making your package more universal. For example, the description of this base package `base.pkg`: ```lua -- Base package base.pkg option("base") -- If current platform is windows, detect ws2_32 link library dependency if os("windows") then add_links("ws2_32") -- If it's other platforms, detect -lm, -ldl, -lpthread dependencies (since they are all system libraries, no search directory is set here) else add_links("m", "dl", "pthread") end ``` If your package is only described through `xmake.lua` without other file directories, you can also embed your package `xmake.lua` description content directly into the project description file `xmake.lua`. These two are originally universal. In other words, the mechanism of `add_packagedirs("pkg")` is to call the project description API: `add_subdirs("pkg/*")` to add sub-projects. And `xxx.pkg` is just a sub-project description file. If you want to add interface detection in your package detection, you only need to use: * `add_cfuncs` * `add_cxxfuncs` * `add_ctypes` * `add_cxxtypes` So using the package mechanism can maximize the reuse of your dependency environment across different projects. It's a very useful feature. --- --- url: /posts/custom-option.md --- xmake can also support some custom option switches, making projects support optional compilation and facilitating modular project management. ## Adding Custom Build Switches Let's take a practical example: We want to add a new switch option called `hello` to our project. If this switch is enabled, it will add some specific source files to the target, but this switch is disabled by default and needs to be linked and used by configuring `xmake f --hello=true`. And when using it, we need to define some special macro definitions: `-DHELLO_TEST -DHELLO_ENABLE`. Then we start modifying `xmake.lua`. The process is not complicated: 1. Define a switch option named `hello` at the top of `xmake.lua` through the `option` interface: ```lua -- Define a switch option named hello, this interface is at the same level as add_target, don't use it inside add_target (it's okay to use it, but it doesn't look good) option("hello") -- Disable this switch by default, need to manually run xmake f --hello=true to enable it, of course you can also enable it by default set_default(false) -- Define some macro switches, these will only be defined when hello is enabled add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") ``` 2. Bind the defined `hello` switch option to your target project: ```lua -- Add a test target target("test") -- Generate executable program set_kind("binary") -- Bind hello switch option add_options("hello") -- Add some source files that are only needed for hello if options("hello") then add_files("hello/*.c") end ``` That's it, just two steps. Next is compilation: ```bash # Direct compilation, hello is disabled by default, so hello-related code is not compiled in $ xmake # Next we enable it and recompile. At this time, hello/*.c code is also compiled in, and -DHELLO_TEST -DHELLO_ENABLE are also added to compilation options $ xmake f --hello=true $ xmake -r ``` Very convenient, right? Just two steps. Next, let's polish it a bit: ```lua option("hello") -- Disable this switch by default, need to manually run xmake f --hello=true to enable it, of course you can also enable it by default set_default(false) -- Define some macro switches, these will only be defined when hello is enabled add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") -- Enable display menu, so when you run xmake f --help, your newly added switch will be displayed set_showmenu(true) -- Categorize the switch in the menu, so the layout will look better when displayed, this is not required set_category("module_xxx") -- In the menu, provide a detailed description of this switch set_description("Enable or disable the hello module") ``` At this time, when you type: ```bash $ xmake f --help ``` The following menu information will be displayed: ``` Omitted here... --hello=HELLO Enable or disable the hello module (default: false) Omitted here... ``` This way, it's clearer for others to see. ## Auto-detection Mechanism Next, let's make it slightly more complex. When this `hello` is enabled, automatically link the `libhello.a` library, and automatically detect `libhello.a`. If it doesn't exist, disable the `hello` switch. Modify as follows: ```lua option("hello") -- Disable this switch by default, need to manually run xmake f --hello=true to enable it, of course you can also enable it by default set_default(false) -- Define some macro switches, these will only be defined when hello is enabled add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") -- Enable display menu, so when you run xmake f --help, your newly added switch will be displayed set_showmenu(true) -- In the menu, provide a detailed description of this switch set_description("Enable or disable the hello module") -- Add link library libhello.a, this will be automatically detected during xmake f, if the link detection fails, this switch will be disabled -- If ok, -lhello will be automatically added during compilation add_links("hello") -- Add link library detection search directory, if the path is wrong, detection will fail to link, if ok, -L./libs will be automatically added during compilation add_linkdirs("libs") ``` After modification, if this `hello` switch is manually enabled or automatically detected successfully, `-L./libs -lhello` link options will be automatically added during compilation and linking. ## Adding Other Detection Rules For auto-detection, in addition to detecting link libraries, you can also add some other detection rules: * Detect whether header files can be included normally * Detect whether type definitions exist * Detect whether interface APIs exist * Detect whether link libraries can be linked normally For example: ```lua option("hello") -- Disable this switch by default, need to manually run xmake f --hello=true to enable it, of course you can also enable it by default set_default(false) -- Define some macro switches, these will only be defined when hello is enabled add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") -- Enable display menu, so when you run xmake f --help, your newly added switch will be displayed set_showmenu(true) -- In the menu, provide a detailed description of this switch set_description("Enable or disable the hello module") -- Add link library libhello.a, this will be automatically detected during xmake f, if the link detection fails, this switch will be disabled -- If ok, -lhello will be automatically added during compilation add_links("hello") -- Add link library detection search directory, if the path is wrong, detection will fail to link, if ok, -L./libs will be automatically added during compilation add_linkdirs("libs") -- Detect in c code: include "hello/hello.h", whether it succeeds, only enable hello if ok -- For c++ code detection, please use: add_cxxincludes add_cincludes("hello/hello.h") -- Add header file detection path, if ok, compilation options will be automatically added: -Iinc/xxx -I./inc add_includedirs("inc/$(plat)", "inc") -- Detect support for c code type wchar_t, if this type doesn't exist, detection fails -- Detection will depend on the header files provided in add_cincludes. If the given header file defines this type, detection will pass -- For c++ code detection, please use: add_cxxtypes add_ctypes("wchar_t") -- Detect whether interface API exists in c code: hello_test() -- Detection will depend on the header files provided in add_cincludes. If the given header file defines this type, detection will pass -- For c++ code detection, please use: add_cxxfuncs add_cfuncs("hello_test") ``` Note that all detections are in AND relationship. All must pass before the `hello` switch is automatically enabled. ## Other APIs That Can Be Automatically Added And after detection passes or is manually enabled, some special compilation options and macro definitions can be automatically added. These interfaces are as follows: * `add_cflags`: After the option switch is enabled, automatically add c compilation options * `add_cxflags`: After the option switch is enabled, automatically add c/c++ compilation options * `add_cxxflags`: After the option switch is enabled, automatically add c++ compilation options * `add_ldflags`: After the option switch is enabled, automatically add link options * `add_vectorexts`: After the option switch is enabled, automatically add instruction extension options, for example: mmx, sse ... ## Auto-generate config.h Configuration File `option` can not only automatically add compilation options during compilation, but also automatically generate various macro switches to the `config.h` file after being enabled, making it convenient for us to control compilation logic in code. For specific usage instructions, see: [Adding Dependencies and Auto-detection Mechanism](https://xmake.io/) --- --- url: /posts/custom-task.md --- `task` is a new feature starting from xmake 2.0 and is also the core of plugin development. In [Plugin Development: Hello xmake](https://xmake.io/) we briefly introduced the definition and usage of tasks. Of course, tasks can not only be used to write plugins, but also to write some simple custom tasks. Let's first look at a simple task implementation: ```lua -- Define a task named hello task("hello") -- Entry point for task execution on_run(function () -- Display hello xmake! print("hello xmake!") end) ``` This is the simplest task. Compared to plugin tasks, it lacks the `set_menu` setting. Of course, you can also add it, so it can be called from the command line. And this `hello` task doesn't have `set_menu` set, so it can only be called in custom scripts. ```lua target("demo") -- Custom clean action on_clean(function(target) -- Import task module import("core.project.task") -- Run this hello task task.run("hello") end) ``` If you want to add parameter passing, there are two ways: 1. Add a command-line option menu through `set_menu`, and access parameters through the `option` module (supports command-line and script parameter passing) 2. Directly pass parameters through scripts Let's first look at the second method, which is simpler. It doesn't need to define a command-line menu. You only need to agree on parameter rules between the task definition and the call site: ```lua -- Direct parameter passing, {} is for the first option parameter passing, leave it empty here -- Here we pass two parameters at the end: arg1, arg2 task.run("hello", {}, "arg1", "arg2") ``` So how to get these two parameters? ```lua -- Define a task named hello task("hello") -- Entry point for task execution, defined with two parameters on_run(function (arg1, arg2) -- Display hello xmake! print("hello xmake: %s %s!", arg1, arg2) end) ``` How simple is that? Of course, this parameter passing method cannot pass parameters externally through the command line, so it's generally used for some built-in task calls. For advanced tasks like plugins, the first parameter passing method is needed. For details, please refer to: [Plugin Development: Parameter Configuration](https://xmake.io/) --- --- url: /guide/best-practices/ai-qa-optimization.md --- # AI Q\&A Optimization {#ai-qa-optimization} When asking questions about xmake to AI assistants (such as ChatGPT, Claude, Cursor, etc.), using some techniques can help AI better understand the context and provide more accurate, higher-quality answers. ## Provide Reference Documentation Links When asking questions, explicitly providing xmake's LLMs reference documentation link can help AI quickly understand xmake's complete API and features. Reference documentation link: ``` Please refer to https://xmake.io/llms-full.txt before answering my question: ... ``` Or more specifically: ``` Please read https://xmake.io/llms-full.txt first to understand xmake's API and features, then answer: How do I configure a target that uses C++20 modules? ``` ## Provide Complete Context Information When asking questions, try to provide complete context information, including: * **Project Type**: Is it a C/C++ project, Swift project, or other language * **Target Platform**: Windows, Linux, macOS, Android, iOS, etc. * **Compiler**: Toolchain used (gcc, clang, msvc, etc.) * **Specific Requirements**: What functionality you want to implement or what problem you want to solve * **Error Messages**: If you encounter problems, provide complete error messages Example: ``` Please refer to https://xmake.io/llms-full.txt to help me solve the following problem: Project Type: C++ project Platform: Linux Compiler: gcc-12 Problem: I want to configure a target in xmake.lua that uses C++20 modules, but I don't know how to set it up. Current xmake.lua content: [Paste your xmake.lua content] ``` ## Reference Specific API Documentation If the question involves specific APIs, you can reference relevant documentation links when asking: ``` Please refer to the target-related APIs in https://xmake.io/llms-full.txt to help me configure: 1. How to set the target's compilation mode (debug/release) 2. How to add precompiled header file support ``` ## Provide Code Examples When asking questions, if possible, provide your current code or configuration: ``` Please refer to https://xmake.io/llms-full.txt to help me optimize the following xmake.lua configuration: target("mytarget") set_kind("binary") add_files("src/*.cpp") I want to add the following features: - Enable C++20 standard - Add precompiled header files - Configure different optimization options for debug and release modes ``` ## Clarify Question Type When asking questions, clearly state the question type: * **Configuration Question**: How to configure a certain feature * **Compilation Question**: Errors encountered during compilation * **Performance Question**: Build speed optimization * **Best Practice**: How to better use a certain feature Example: ``` Please refer to https://xmake.io/llms-full.txt. This is a configuration question: I want to configure CUDA project compilation in xmake, and need: 1. Specify CUDA SDK version 2. Set GPU architecture 3. Configure compilation options ``` ## Ask Step by Step For complex questions, you can ask step by step: ``` Please refer to https://xmake.io/llms-full.txt to help me configure step by step: Step 1: How to create a basic C++ target Step 2: How to add dependency packages Step 3: How to configure cross-compilation ``` ## Verify Answer Accuracy AI answers may not be completely accurate. It is recommended to: 1. **Consult Official Documentation**: Verify whether the APIs and usage provided by AI are correct. You can refer to the [API Reference](/api/description/specification) and [Guide](/guide/introduction) 2. **Actually Test**: Actually test the configuration provided by AI in your project 3. **Cross-verify**: If possible, ask questions in different ways to verify answer consistency ## Example: Complete Question Template ``` Please refer to https://xmake.io/llms-full.txt to understand xmake's complete API and features. Project Information: - Type: C++ project - Platform: Linux - Compiler: clang-15 - Standard: C++20 Current Problem: I want to configure a target that uses C++20 modules, but encountered a compilation error. Current Configuration: target("mymodule") set_kind("binary") set_languages("c++20") add_files("src/*.cpp") Error Message: [Paste error message] Please help me: 1. Analyze the cause of the problem 2. Provide the correct configuration method 3. Give a complete example code ``` Through the above methods, you can help AI better understand your needs and provide more accurate and useful answers. --- --- url: /zh/guide/best-practices/ai-qa-optimization.md --- # AI 问答优化 {#ai-qa-optimization} 在使用 AI 助手(如 ChatGPT、Claude、Cursor 等)提问关于 xmake 的问题时,通过一些技巧可以帮助 AI 更好地理解上下文,提供更准确、更高质量的回答。 ## 提供参考文档链接 在提问时,显式提供 xmake 的 LLMs 参考文档链接,可以帮助 AI 快速了解 xmake 的完整 API 和功能。 参考文档链接: ``` 请参考 https://xmake.io/llms-full.txt 后,回答我的问题:... ``` 或者更具体地: ``` 请先阅读 https://xmake.io/llms-full.txt 了解 xmake 的 API 和功能,然后回答: 如何配置一个使用 C++20 模块的目标? ``` ## 提供完整的上下文信息 在提问时,尽量提供完整的上下文信息,包括: * **项目类型**:是 C/C++ 项目、Swift 项目还是其他语言 * **目标平台**:Windows、Linux、macOS、Android、iOS 等 * **编译器**:使用的工具链(gcc、clang、msvc 等) * **具体需求**:想要实现什么功能或解决什么问题 * **错误信息**:如果遇到问题,提供完整的错误信息 示例: ``` 请参考 https://xmake.io/llms-full.txt,帮我解决以下问题: 项目类型:C++ 项目 平台:Linux 编译器:gcc-12 问题:我想在 xmake.lua 中配置一个使用 C++20 模块的目标,但不知道如何设置。 当前的 xmake.lua 内容: [粘贴你的 xmake.lua 内容] ``` ## 引用具体的 API 文档 如果问题涉及特定的 API,可以在提问时引用相关的文档链接: ``` 请参考 https://xmake.io/llms-full.txt 中的 target 相关 API,帮我配置: 1. 如何设置目标的编译模式(debug/release) 2. 如何添加预编译头文件支持 ``` ## 提供代码示例 在提问时,如果可能,提供你当前的代码或配置: ``` 请参考 https://xmake.io/llms-full.txt,帮我优化以下 xmake.lua 配置: target("mytarget") set_kind("binary") add_files("src/*.cpp") 我想添加以下功能: - 启用 C++20 标准 - 添加预编译头文件 - 配置 debug 和 release 模式的不同优化选项 ``` ## 明确问题类型 在提问时,明确说明问题的类型: * **配置问题**:如何配置某个功能 * **编译问题**:编译时遇到的错误 * **性能问题**:构建速度优化 * **最佳实践**:如何更好地使用某个特性 示例: ``` 请参考 https://xmake.io/llms-full.txt,这是一个配置问题: 我想在 xmake 中配置 CUDA 项目的编译,需要: 1. 指定 CUDA SDK 版本 2. 设置 GPU 架构 3. 配置编译选项 ``` ## 分步骤提问 对于复杂的问题,可以分步骤提问: ``` 请参考 https://xmake.io/llms-full.txt,分步骤帮我配置: 第一步:如何创建一个基本的 C++ 目标 第二步:如何添加依赖包 第三步:如何配置交叉编译 ``` ## 验证回答的准确性 AI 的回答可能不完全准确,建议: 1. **查阅官方文档**:验证 AI 提供的 API 和用法是否正确,可参考 [API 手册](/zh/api/description/specification) 和 [使用指南](/zh/guide/introduction) 2. **实际测试**:在项目中实际测试 AI 提供的配置 3. **交叉验证**:如果可能,用不同的方式提问验证答案的一致性 ## 示例:完整的提问模板 ``` 请参考 https://xmake.io/llms-full.txt 了解 xmake 的完整 API 和功能。 项目信息: - 类型:C++ 项目 - 平台:Linux - 编译器:clang-15 - 标准:C++20 当前问题: 我想配置一个使用 C++20 模块的目标,但遇到了编译错误。 当前配置: target("mymodule") set_kind("binary") set_languages("c++20") add_files("src/*.cpp") 错误信息: [粘贴错误信息] 请帮我: 1. 分析问题原因 2. 提供正确的配置方法 3. 给出完整的示例代码 ``` 通过以上方式,可以帮助 AI 更好地理解你的需求,提供更准确、更有用的回答。 --- --- url: /api/scripts/extension-modules/async/jobgraph.md --- # async.jobgraph This module provides a job graph (DAG) for advanced asynchronous job scheduling and dependency management in xmake Lua scripts. ## jobgraph.new * Create a new job graph instance. #### Function Prototype ::: tip API ```lua jobgraph.new() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua import("async.jobgraph") local jobs = jobgraph.new() ``` > All following examples assume `async.jobgraph` has been imported. ## jobgraph:add * Add a job node to the job graph. #### Function Prototype ::: tip API ```lua jobgraph:add(name: , jobfunc: , options: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Job name string | | jobfunc | Job function | | options | Options table (optional) | #### Usage ```lua local jobs = jobgraph.new() jobs:add("job/root", function() print("root job") end) jobs:add("job/child", function() print("child job") end) ``` You can also assign a job to a group or multiple groups using the `groups` option: ```lua local jobs = jobgraph.new() -- Add a job to a single group jobs:add("foo/buildfiles", function(index, total, opt) -- build logic end, {groups = "foo/buildfiles"}) -- Add a job to multiple groups jobs:add("xxx", function(index, total, opt) -- ... end, {groups = {"group1", "group2"}}) ``` ::: tip NOTE For batch group operations, it is generally more recommended and convenient to use `jobgraph:group`. See: [jobgraph:group](#jobgraph-group) ::: A typical use case in a rule: ```lua rule("foo") on_build_files(function (target, jobgraph, sourcebatch, opt) local group_name = target:name() .. "/buildfiles" for _, sourcefile in ipairs(sourcebatch.sourcefiles) do local job_name = target:name() .. "/" .. sourcefile jobgraph:add(job_name, function(index, total, opt) -- TODO: build file end, {groups = group_name}) end -- add job orders, other target jobs -> this build group jobgraph:add_orders(other_target:name() .. "/buildfiles", group_name) end, {jobgraph = true}) ``` ## jobgraph:add\_orders * Add dependency orders. #### Function Prototype ::: tip API ```lua jobgraph:add_orders(jobname: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | jobname | Job name string | | ... | Variable arguments for dependency jobs | #### Usage ```lua local jobs = jobgraph.new() jobs:add("job/root", function() print("root job") end) jobs:add("job/child", function() print("child job") end) jobs:add_orders("job/child", "job/root") ``` ## jobgraph:group * Group jobs for batch dependency management. You can use a callback to add jobs to a group. #### Function Prototype ::: tip API ```lua jobgraph:group(groupname: , callback: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | groupname | Group name string | | callback | Callback function to add jobs to group | #### Usage ```lua local jobs = jobgraph.new() jobs:add("job", function(index, total, opt) -- ... end) jobs:group("group1", function() for i = 0, N do jobs:add("group1/job" .. i, function(index, total, opt) -- TODO end) end end) jobs:group("group2", function() for i = 0, N do jobs:add("group2/job" .. i, function(index, total, opt) -- TODO end) end end) -- sort job orders after adding all these jobs jobs:add_orders("job", "group1", "group2") ``` See also: [async.runjobs](/api/scripts/extension-modules/async/runjobs) --- --- url: /zh/api/scripts/extension-modules/async/jobgraph.md --- # async.jobgraph 此模块为 xmake 的 Lua 脚本提供任务图(DAG),用于高级异步任务调度与依赖管理。 ## jobgraph.new * 创建新的任务图实例。 #### 函数原型 ::: tip API ```lua jobgraph.new() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua import("async.jobgraph") local jobs = jobgraph.new() ``` > 下方所有示例均假定已 import("async.jobgraph")。 ## jobgraph:add * 向任务图添加一个任务节点。 #### 函数原型 ::: tip API ```lua jobgraph:add(name: , jobfunc: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 任务名称字符串 | | jobfunc | 任务函数 | | options | 选项表(可选) | #### 用法说明 ```lua local jobs = jobgraph.new() jobs:add("job/root", function() print("root job") end) jobs:add("job/child", function() print("child job") end) ``` 如需将任务加入分组,可通过 `groups` 选项实现: ```lua local jobs = jobgraph.new() -- 添加到单个分组 jobs:add("foo/buildfiles", function(index, total, opt) -- 构建逻辑 end, {groups = "foo/buildfiles"}) -- 添加到多个分组 jobs:add("xxx", function(index, total, opt) -- ... end, {groups = {"group1", "group2"}}) ``` ::: tip 提示 对于批量分组场景,更推荐使用 `jobgraph:group`,更为方便。 详见:[jobgraph:group](#jobgraph-group) ::: 典型用法(如 rule 中): ```lua rule("foo") on_build_files(function (target, jobgraph, sourcebatch, opt) local group_name = target:name() .. "/buildfiles" for _, sourcefile in ipairs(sourcebatch.sourcefiles) do local job_name = target:name() .. "/" .. sourcefile jobgraph:add(job_name, function(index, total, opt) -- TODO: 构建文件 end, {groups = group_name}) end -- 添加依赖关系,其他 target 的 job -> 当前 build group jobgraph:add_orders(other_target:name() .. "/buildfiles", group_name) end, {jobgraph = true}) ``` ## jobgraph:add\_orders * 添加依赖执行顺序 #### 函数原型 ::: tip API ```lua jobgraph:add_orders(jobname: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | jobname | 任务名称字符串 | | ... | 依赖任务的可变参数 | #### 用法说明 ```lua local jobs = jobgraph.new() jobs:add("job/root", function() print("root job") end) jobs:add("job/child", function() print("child job") end) jobs:add_orders("job/child", "job/root") ``` ## jobgraph:group * 分组批量管理任务依赖。可通过回调批量添加分组任务 #### 函数原型 ::: tip API ```lua jobgraph:group(groupname: , callback: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | groupname | 分组名称字符串 | | callback | 向分组添加任务的回调函数 | #### 用法说明 ```lua local jobs = jobgraph.new() jobs:add("job", function(index, total, opt) -- ... end) jobs:group("group1", function() for i = 0, N do jobs:add("group1/job" .. i, function(index, total, opt) -- TODO end) end end) jobs:group("group2", function() for i = 0, N do jobs:add("group2/job" .. i, function(index, total, opt) -- TODO end) end end) -- 添加所有分组后统一排序依赖 jobs:add_orders("job", "group1", "group2") ``` 相关链接:[async.runjobs](/zh/api/scripts/extension-modules/async/runjobs) --- --- url: /api/scripts/extension-modules/async/runjobs.md --- # async.runjobs This module runs all jobs in a job graph concurrently, respecting dependencies. ## runjobs Run all jobs in the job graph concurrently. #### Function Prototype ::: tip API ```lua runjobs(name: , jobs: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Job name string | | jobs | Job graph instance | | options | Options table | #### Usage * `comax`: Maximum number of concurrent jobs. * `timeout`: Timeout for each job (ms). * `timer`: Timer callback for monitoring running jobs. ```lua import("core.base.scheduler") import("async.jobgraph") import("async.runjobs") function jobfunc(index, total, opt) print("%s: run job (%d/%d)", scheduler.co_running(), index, total) os.sleep(1000) print("%s: run job (%d/%d) end, progress: %s", scheduler.co_running(), index, total, opt.progress) end local jobs = jobgraph.new() jobs:add("job/root", jobfunc) for i = 1, 3 do jobs:add("job/" .. i, jobfunc) for j = 1, 5 do jobs:add("job/" .. i .. "/" .. j, jobfunc) jobs:add_orders("job/" .. i .. "/" .. j, "job/" .. i, "job/root") end end runjobs("test", jobs, { comax = 4, timeout = 1000, timer = function (running_jobs_indices) print("timeout, running: %s", table.concat(running_jobs_indices, ",")) end }) ``` See also: [async.jobgraph](/api/scripts/extension-modules/async/jobgraph) --- --- url: /zh/api/scripts/extension-modules/async/runjobs.md --- # async.runjobs 此模块用于并发执行任务图中的所有任务,自动处理依赖关系。 ## runjobs 并发执行任务图中的所有任务。 #### 函数原型 ::: tip API ```lua runjobs(name: , jobs: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 任务名称字符串 | | jobs | 任务图实例 | | options | 选项表 | #### 用法说明 * `comax`: 最大并发任务数 * `timeout`: 单个任务超时时间(毫秒) * `timer`: 定时回调,可用于监控当前运行中的任务 ```lua import("core.base.scheduler") import("async.jobgraph") import("async.runjobs") function jobfunc(index, total, opt) print("%s: run job (%d/%d)", scheduler.co_running(), index, total) os.sleep(1000) print("%s: run job (%d/%d) end, progress: %s", scheduler.co_running(), index, total, opt.progress) end local jobs = jobgraph.new() jobs:add("job/root", jobfunc) for i = 1, 3 do jobs:add("job/" .. i, jobfunc) for j = 1, 5 do jobs:add("job/" .. i .. "/" .. j, jobfunc) jobs:add_orders("job/" .. i .. "/" .. j, "job/" .. i, "job/root") end end runjobs("test", jobs, { comax = 4, timeout = 1000, timer = function (running_jobs_indices) print("timeout, running: %s", table.concat(running_jobs_indices, ",")) end }) ``` 相关链接:[async.jobgraph](/zh/api/scripts/extension-modules/async/jobgraph) --- --- url: /guide/extras/autoscan-sourcecode.md --- # Auto-scan Source Build Xmake supports the autogeneration of project files, including its own! While it won't work for every project (as is the nature of these things), it should work for basic and medium complexity projects. You do not need to write any "make-like" file (`xmake.lua`, `Makefile`, `CMakeLists.txt`, etc.). The tool will scan all of the source files and generate an `xmake.lua` automatically based on your project structure. Xmake will try to detect a `main` function in the source files to distinguish between the source code for libraries and executable programs. If autogeneration succeeds, you should still look through the generated `xmake.lua` and make any changes you need, and make sure everything worked well. Currently, projects that use directories in multiple levels are *not* supported. Apologies. ## Use cases 1. Temporarily quickly compile and run some scattered test code 2. A starting point to porting and compiling open source code 3. Quickly create a new Xmake project based on existing code ## How to use it Execute Xmake directly in the directory with the source code (no xmake.lua), and follow the prompts: ```sh $ xmake note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)? please input: n (y/n) y ``` In addition, when there are other build system identification files (such as `CMakeLists.txt`), the process of automatically generating an `xmake.lua` file will not be triggered. Instead, it will attempt to automatically detect build system and compile the code. If you want to force trigger the process of automatically generating `xmake.lua` file, you can run: ```sh $ xmake f -y ``` ## Compile open source libraries Although this approach has some limitations, it is already sufficient to generate for existing libraries. For example, if you download the source code for zlib-1.2.10 and want to compile it, you only need to enter the zlib source directory and run the following command: ```sh $ cd zlib-1.2.10 $ xmake note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)? please input: n (y/n) y ``` It's done! the output results: ``` target(zlib-1.2): static [+]: ./adler32.c [+]: ./compress.c [+]: ./crc32.c [+]: ./deflate.c [+]: ./gzclose.c [+]: ./gzlib.c [+]: ./gzread.c [+]: ./gzwrite.c [+]: ./infback.c [+]: ./inffast.c [+]: ./inflate.c [+]: ./inftrees.c [+]: ./trees.c [+]: ./uncompr.c [+]: ./zutil.c xmake.lua generated, scan ok!👌 checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.12 checking for the target minimal version ... 10.12 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the debugger (dd) ... xcrun -sdk macosx lldb checking for the golang compiler (go) ... go configure { ex = "xcrun -sdk macosx ar" , sh = "xcrun -sdk macosx clang++" , host = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , plat = "macosx" , xcode_dir = "/Applications/Xcode.app" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , go = "go" , target_minver = "10.12" , ccache = "ccache" , mode = "release" , clean = true , cxx = "xcrun -sdk macosx clang" , cc = "xcrun -sdk macosx clang" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.12" , sc = "xcrun -sdk macosx swiftc" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: cache compiling.release ./adler32.c [06%]: cache compiling.release ./compress.c [13%]: cache compiling.release ./crc32.c [20%]: cache compiling.release ./deflate.c [26%]: cache compiling.release ./gzclose.c [33%]: cache compiling.release ./gzlib.c [40%]: cache compiling.release ./gzread.c [46%]: cache compiling.release ./gzwrite.c [53%]: cache compiling.release ./infback.c [60%]: cache compiling.release ./inffast.c [66%]: cache compiling.release ./inflate.c [73%]: cache compiling.release ./inftrees.c [80%]: cache compiling.release ./trees.c [86%]: cache compiling.release ./uncompr.c [93%]: cache compiling.release ./zutil.c [100%]: archiving.release libzlib-1.2.a build ok!👌 ``` Xmake will scan the current directory to detect all source codes and it do not found main function. As such, detect that it is a static library, and thus it will build it as a static library (with an output/artifact of `libzlib-1.2.a`). We did not write any make-like files (`xmake.lua`, etc.) and did not use the makefile of the zlib project. Isn't that neat? It is compiled directly and an `xmake.lua` file was generated, which we can edit to build a more complicated project. The content of the generated xmake.lua: ```lua -- define target target("zlib-1.2") -- set kind set_kind("static") -- add files add_files("./adler32.c") add_files("./compress.c") add_files("./crc32.c") add_files("./deflate.c") add_files("./gzclose.c") add_files("./gzlib.c") add_files("./gzread.c") add_files("./gzwrite.c") add_files("./infback.c") add_files("./inffast.c") add_files("./inflate.c") add_files("./inftrees.c") add_files("./trees.c") add_files("./uncompr.c") add_files("./zutil.c") ``` As you can see, it's pretty human readable. ## Compile and run testing code... fast! Let's say you want to write a simple program, with one source file (`main.c`), solely to print "Hello, world!" to stdout. ```c /* main.c */ #include #include int main(int argc, char *argv[]) { printf("Hello, world!"); return EXIT_SUCCESS; } ``` If we use GCC to compile and run it, we need to run two commands: ```sh $ gcc ./main.c -o main $ ./main ``` If we use xmake to run it, we only need to run: ```sh $ xmake run ``` Or even: ```sh $ xmake r ``` As we expect, we see... ``` Hello, world! ``` ...printed to the console! Even if we have a lot of source files, you only need to run one command: ```sh $ xmake run ``` How easy and convenient! ## Multi-language support This feature of autogeneration of project files not only supports C/C++, also supports Objective-C and Swift, and it will support Go in future. For example, if you download the `fmdb` library, an iOS library which wraps SQLite: ``` # Files: . ├── FMDB.h ├── FMDatabase.h ├── FMDatabase.m ├── FMDatabaseAdditions.h ├── FMDatabaseAdditions.m ├── FMDatabasePool.h ├── FMDatabasePool.m ├── FMDatabaseQueue.h ├── FMDatabaseQueue.m ├── FMResultSet.h └── FMResultSet.m ``` You can see that there aren't any make-like files in the project directory. "Whatever will we do?" I think you know. We can use Xmake to build it directly as a iOS static library: ```sh $ xmake f -p iphoneos; xmake ``` The output is: ``` xmake.lua not found, scanning files .. target(FMDB): static [+]: ./FMDatabase.m [+]: ./FMDatabaseAdditions.m [+]: ./FMDatabasePool.m [+]: ./FMDatabaseQueue.m [+]: ./FMResultSet.m xmake.lua generated, scan ok!👌 checking for the architecture ... armv7 checking for the Xcode SDK version for iphoneos ... 10.1 checking for the target minimal version ... 10.1 checking for the c compiler (cc) ... xcrun -sdk iphoneos clang checking for the c++ compiler (cxx) ... xcrun -sdk iphoneos clang checking for the objc compiler (mm) ... xcrun -sdk iphoneos clang checking for the objc++ compiler (mxx) ... xcrun -sdk iphoneos clang++ checking for the assember (as) ... gas-preprocessor.pl xcrun -sdk iphoneos clang checking for the linker (ld) ... xcrun -sdk iphoneos clang++ checking for the static library archiver (ar) ... xcrun -sdk iphoneos ar checking for the static library extractor (ex) ... xcrun -sdk iphoneos ar checking for the shared library linker (sh) ... xcrun -sdk iphoneos clang++ checking for the swift compiler (sc) ... xcrun -sdk iphoneos swiftc configure { ex = "xcrun -sdk iphoneos ar" , ccache = "ccache" , host = "macosx" , ar = "xcrun -sdk iphoneos ar" , buildir = "build" , as = "/usr/local/share/xmake/tools/utils/gas-preprocessor.pl xcrun -sdk iphoneos clang" , arch = "armv7" , mxx = "xcrun -sdk iphoneos clang++" , cxx = "xcrun -sdk iphoneos clang" , target_minver = "10.1" , xcode_dir = "/Applications/Xcode.app" , clean = true , sh = "xcrun -sdk iphoneos clang++" , cc = "xcrun -sdk iphoneos clang" , ld = "xcrun -sdk iphoneos clang++" , mode = "release" , kind = "static" , plat = "iphoneos" , xcode_sdkver = "10.1" , sc = "xcrun -sdk iphoneos swiftc" , mm = "xcrun -sdk iphoneos clang" } configure ok! clean ok! [00%]: cache compiling.release ./FMDatabase.m [20%]: cache compiling.release ./FMDatabaseAdditions.m [40%]: cache compiling.release ./FMDatabasePool.m [60%]: cache compiling.release ./FMDatabaseQueue.m [80%]: cache compiling.release ./FMResultSet.m [100%]: archiving.release libFMDB.a build ok!👌 ``` and of course we also get a `libFMDB.a` static library. ## Compile multiple executables at the same time Let's say you downloaded the "sixth public release of the Independent JPEG Group's free JPEG software", and wanted to build it. You could do it yourself, or you could run: ```sh xmake ``` The output results are: ``` xmake.lua not found, scanning files .. target(jpeg-6b): static [+]: ./cdjpeg.c [+]: ./example.c [+]: ./jcapimin.c [+]: ./jcapistd.c [+]: ./jccoefct.c [+]: ./jccolor.c [+]: ./jcdctmgr.c [+]: ./jchuff.c [+]: ./jcinit.c [+]: ./jcmainct.c [+]: ./jcmarker.c [+]: ./jcmaster.c [+]: ./jcomapi.c [+]: ./jcparam.c [+]: ./jcphuff.c [+]: ./jcprepct.c [+]: ./jcsample.c [+]: ./jctrans.c [+]: ./jdapimin.c [+]: ./jdapistd.c [+]: ./jdatadst.c [+]: ./jdatasrc.c [+]: ./jdcoefct.c [+]: ./jdcolor.c [+]: ./jddctmgr.c [+]: ./jdhuff.c [+]: ./jdinput.c [+]: ./jdmainct.c [+]: ./jdmarker.c [+]: ./jdmaster.c [+]: ./jdmerge.c [+]: ./jdphuff.c [+]: ./jdpostct.c [+]: ./jdsample.c [+]: ./jdtrans.c [+]: ./jerror.c [+]: ./jfdctflt.c [+]: ./jfdctfst.c [+]: ./jfdctint.c [+]: ./jidctflt.c [+]: ./jidctfst.c [+]: ./jidctint.c [+]: ./jidctred.c [+]: ./jmemansi.c [+]: ./jmemmgr.c [+]: ./jmemname.c [+]: ./jmemnobs.c [+]: ./jquant1.c [+]: ./jquant2.c [+]: ./jutils.c [+]: ./rdbmp.c [+]: ./rdcolmap.c [+]: ./rdgif.c [+]: ./rdppm.c [+]: ./rdrle.c [+]: ./rdswitch.c [+]: ./rdtarga.c [+]: ./transupp.c [+]: ./wrbmp.c [+]: ./wrgif.c [+]: ./wrppm.c [+]: ./wrrle.c [+]: ./wrtarga.c target(ansi2knr): binary [+]: ./ansi2knr.c target(cjpeg): binary [+]: ./cjpeg.c target(ckconfig): binary [+]: ./ckconfig.c target(djpeg): binary [+]: ./djpeg.c target(jpegtran): binary [+]: ./jpegtran.c target(rdjpgcom): binary [+]: ./rdjpgcom.c target(wrjpgcom): binary [+]: ./wrjpgcom.c xmake.lua generated, scan ok!👌 checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.12 checking for the target minimal version ... 10.12 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the debugger (dd) ... xcrun -sdk macosx lldb checking for the golang compiler (go) ... go configure { ex = "xcrun -sdk macosx ar" , sh = "xcrun -sdk macosx clang++" , host = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , plat = "macosx" , xcode_dir = "/Applications/Xcode.app" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , go = "go" , target_minver = "10.12" , ccache = "ccache" , mode = "release" , clean = true , cxx = "xcrun -sdk macosx clang" , cc = "xcrun -sdk macosx clang" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.12" , sc = "xcrun -sdk macosx swiftc" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: cache compiling.release ./cdjpeg.c [00%]: cache compiling.release ./example.c [00%]: cache compiling.release ./jcapimin.c [00%]: cache compiling.release ./jcapistd.c [00%]: cache compiling.release ./jccoefct.c [00%]: cache compiling.release ./jccolor.c [01%]: cache compiling.release ./jcdctmgr.c [01%]: cache compiling.release ./jchuff.c [01%]: cache compiling.release ./jcinit.c [01%]: cache compiling.release ./jcmainct.c [01%]: cache compiling.release ./jcmarker.c [02%]: cache compiling.release ./jcmaster.c [02%]: cache compiling.release ./jcomapi.c [02%]: cache compiling.release ./jcparam.c [02%]: cache compiling.release ./jcphuff.c [02%]: cache compiling.release ./jcprepct.c [03%]: cache compiling.release ./jcsample.c [03%]: cache compiling.release ./jctrans.c [03%]: cache compiling.release ./jdapimin.c [03%]: cache compiling.release ./jdapistd.c [03%]: cache compiling.release ./jdatadst.c [04%]: cache compiling.release ./jdatasrc.c [04%]: cache compiling.release ./jdcoefct.c [04%]: cache compiling.release ./jdcolor.c [04%]: cache compiling.release ./jddctmgr.c [04%]: cache compiling.release ./jdhuff.c [05%]: cache compiling.release ./jdinput.c [05%]: cache compiling.release ./jdmainct.c [05%]: cache compiling.release ./jdmarker.c [05%]: cache compiling.release ./jdmaster.c [05%]: cache compiling.release ./jdmerge.c [06%]: cache compiling.release ./jdphuff.c [06%]: cache compiling.release ./jdpostct.c [06%]: cache compiling.release ./jdsample.c [06%]: cache compiling.release ./jdtrans.c [06%]: cache compiling.release ./jerror.c [07%]: cache compiling.release ./jfdctflt.c [07%]: cache compiling.release ./jfdctfst.c [07%]: cache compiling.release ./jfdctint.c [07%]: cache compiling.release ./jidctflt.c [07%]: cache compiling.release ./jidctfst.c [08%]: cache compiling.release ./jidctint.c [08%]: cache compiling.release ./jidctred.c [08%]: cache compiling.release ./jmemansi.c [08%]: cache compiling.release ./jmemmgr.c [08%]: cache compiling.release ./jmemname.c [09%]: cache compiling.release ./jmemnobs.c [09%]: cache compiling.release ./jquant1.c [09%]: cache compiling.release ./jquant2.c [09%]: cache compiling.release ./jutils.c [09%]: cache compiling.release ./rdbmp.c [10%]: cache compiling.release ./rdcolmap.c [10%]: cache compiling.release ./rdgif.c [10%]: cache compiling.release ./rdppm.c [10%]: cache compiling.release ./rdrle.c [10%]: cache compiling.release ./rdswitch.c [11%]: cache compiling.release ./rdtarga.c [11%]: cache compiling.release ./transupp.c [11%]: cache compiling.release ./wrbmp.c [11%]: cache compiling.release ./wrgif.c [11%]: cache compiling.release ./wrppm.c [12%]: cache compiling.release ./wrrle.c [12%]: cache compiling.release ./wrtarga.c [12%]: archiving.release libjpeg-6b.a [12%]: cache compiling.release ./wrjpgcom.c [25%]: linking.release wrjpgcom [25%]: cache compiling.release ./ansi2knr.c [37%]: linking.release ansi2knr [37%]: cache compiling.release ./jpegtran.c [50%]: linking.release jpegtran [50%]: cache compiling.release ./djpeg.c [62%]: linking.release djpeg [62%]: cache compiling.release ./ckconfig.c [75%]: linking.release ckconfig [75%]: cache compiling.release ./rdjpgcom.c [87%]: linking.release rdjpgcom [87%]: cache compiling.release ./cjpeg.c [100%]: linking.release cjpeg build ok!👌 ``` In addition to a static library, we also compiled some other executable programs. ``` target(ansi2knr): binary [+]: ./ansi2knr.c target(cjpeg): binary [+]: ./cjpeg.c target(ckconfig): binary [+]: ./ckconfig.c target(djpeg): binary [+]: ./djpeg.c target(jpegtran): binary [+]: ./jpegtran.c target(rdjpgcom): binary [+]: ./rdjpgcom.c target(wrjpgcom): binary [+]: ./wrjpgcom.c ``` Neat! ## Manual configuration If we want to add some manual configuration options. we need add them before compiling. For example: ```sh # Specify our options $ xmake f --cxflags="--cxx-flag" --ldflags="--link-flag" --includedirs="include/" --linkdirs="lib/" # Build! $ xmake ``` --- --- url: /examples/cpp/autogen.md --- # Automatic Code Generation {#autogen} In many cases, we need to preprocess and automatically generate code before compilation, and then include the generated code in the subsequent build process. As Xmake continues to evolve, its support for code generation features is also being updated and improved. Currently, the following approaches are supported: ## The Simplest Approach This is the most straightforward method: simply generate the code before building and forcibly add it using `add_files`. By default, `add_files` will not add files that do not exist, so you need to set `always_added = true` to force the addition, even if the file does not exist yet. ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("$(builddir)/autogen.cpp", {always_added = true}) before_build(function (target) io.writefile("$(builddir)/autogen.cpp", [[ #include using namespace std; int main(int argc, char** argv) { cout << "hello world!" << endl; return 0; } ]]) end) ``` This approach has many limitations and is not commonly used in real-world scenarios, but it is simple and easy to understand. ## Generation via Dependent Target Sometimes, code generation requires running a target program within the project. This can be achieved as follows: ```lua add_rules("mode.debug", "mode.release") rule("autogen") set_extensions(".in") before_buildcmd_file(function (target, batchcmds, sourcefile, opt) local sourcefile_cx = path.join(target:autogendir(), "rules", "autogen", path.basename(sourcefile) .. ".cpp") local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) batchcmds:show_progress(opt.progress, "${color.build.object}compiling.autogen %s", sourcefile) batchcmds:mkdir(path.directory(sourcefile_cx)) batchcmds:vrunv(target:dep("autogen"):targetfile(), {sourcefile, sourcefile_cx}) batchcmds:compile(sourcefile_cx, objectfile) batchcmds:add_depfiles(sourcefile, target:dep("autogen"):targetfile()) batchcmds:set_depmtime(os.mtime(objectfile)) batchcmds:set_depcache(target:dependfile(objectfile)) end) target("autogen") set_default(false) set_kind("binary") set_plat(os.host()) -- Build for the host platform set_arch(os.arch()) add_files("src/autogen.cpp") set_languages("c++11") set_policy("build.fence", true) -- Disable parallel compilation between source files target("test") set_kind("binary") add_deps("autogen") add_rules("autogen") add_files("src/main.cpp") add_files("src/*.in") ``` First, configure an `autogen` target program that must be runnable on the current build platform, so use `set_plat(os.host())` to force building for the host platform. Also, set the `build.fence` policy to disable parallel compilation between source files, ensuring the `autogen` target is built first and its executable is available in advance. Then, define a custom rule to invoke the `autogen` target program before building, generate the source code, and compile the generated code into object files for linking. See the full example: [autogen\_codedep](https://github.com/xmake-io/xmake/blob/dev/tests/projects/other/autogen/autogen_codedep/xmake.lua) ## Generation via Native Module Xmake introduces native module development, allowing code generation via native modules without defining an extra `autogen` target program. For details on native module development, see: [Native Module Development](/api/scripts/native-modules). ```lua add_rules("mode.debug", "mode.release") add_moduledirs("modules") rule("autogen") set_extensions(".in") before_build_file(function (target, sourcefile, opt) import("utils.progress") import("core.project.depend") import("core.tool.compiler") import("autogen.foo", {always_build = true}) local sourcefile_cx = path.join(target:autogendir(), "rules", "autogen", path.basename(sourcefile) .. ".cpp") local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) depend.on_changed(function () progress.show(opt.progress, "${color.build.object}compiling.autogen %s", sourcefile) os.mkdir(path.directory(sourcefile_cx)) foo.generate(sourcefile, sourcefile_cx) compiler.compile(sourcefile_cx, objectfile, {target = target}) end, {dependfile = target:dependfile(objectfile), files = sourcefile, changed = target:is_rebuilt()}) end) target("test") set_kind("binary") add_rules("autogen") add_files("src/main.cpp") add_files("src/*.in") ``` See the full example: [Native Module Autogen](https://github.com/xmake-io/xmake/blob/dev/tests/projects/other/autogen/autogen_shared_module/xmake.lua). ## Generation in the Prepare Phase Since Xmake 3.x, the `on_prepare` and `on_prepare_files` interfaces have been introduced to enable two-phase builds. The Prepare phase can be used specifically for source code generation and preprocessing. The Prepare phase is executed before all `on_build` and `on_build_files` interfaces. For details, see: [Prepare Interface Manual](/api/description/project-target.html#on-prepare). --- --- url: /guide/extras/build-cache.md --- # Build Cache Acceleration Xmake supports various methods of caching in the build process, which can speed up your builds. ## Local compilation cache By default, Xmake will enable the local cache. The version before 2.6.5 uses the external ccache by default, and after 2.6.6, Xmake provides a built-in cross-platform local cache solution. Compared with third-party independent processes such as ccache, Xmake's internal state maintenance is easier to optimize, and it also avoids frequent independent process loading and time consumption, and avoids additional communication with the daemon process. In addition, the built-in cache works better cross-platform, and it also supports MSVC on Windows well, while ccache only supports the GCC and Clang toolchains. If it's causing you problems, you can also disable the cache with the following command. ```sh $ xmake f --ccache=n ``` Note: Regardless of whether the built-in local cache is used, the configuration name is `--ccache=`, which means the C/C++ build cache, not just the name of the ccache tool. If we want to continue to use other external caching tools, we can also configure them in the following way. ```sh $ xmake f --ccache=n --cxx="ccache gcc" --cc="ccache gcc" $ xmake ``` ## Remote compilation cache In addition to local caching, we also provide remote caching services, similar to Mozilla's sscache, which is usually not used if it is only for personal development. However, if a large-scale project is developed collaboratively by multiple people within an organization, distributed compilation and local caching alone are not enough. We also need to cache the compiled object files to a separate server for sharing. This way, even if other people compile it for the first time, they do not need to compile it every time, and instead can directly pull the cache from the remote to speed up the compilation. In addition, the remote cache service provided by Xmake is also supported on all platforms, not only GCC and Clang, but also MSVC. ### Start the service We can specify the `--ccache` parameter to enable the remote compilation cache service. Of course, if this parameter is not specified, Xmake will enable all server-configured services by default. ```sh $ xmake service --ccache : listening 0.0.0.0:9092 .. ``` We can also start the service and display detailed log information with the `-vD` flag. ```sh $ xmake service --ccache -vD : listening 0.0.0.0:9092 .. ``` ### Start the service in Daemon mode To start, restart, or stop the cache service in daemon mode, you can issue the below commands: ```sh $ xmake service --ccache --start $ xmake service --ccache --restart $ xmake service --ccache --stop ``` ### Configure the server To configure the server, you must first generate the configuration file, or grab one off the internet (the first option is easier). To do so, run the `xmake service` command, and it will automatically generate a default `server.conf` configuration file, stored in `~/.xmake/service/server.conf`. ```sh $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. a token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9692 .. ``` Then, we edit it, fixing the server's listening port (optional). ```sh { distcc_build = { listen = "0.0.0.0:9692", workdir = "/Users/ruki/.xmake/service/server/remote_cache" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` ### Configure the client The client configuration file is in `~/.xmake/service/client.conf`, where you can specify where to connect to. We can configure multiple server addresses and corresponding tokens in the hosts list. ```sh { remote_cache = { connect = "127.0.0.1:9692", token = "590234653af52e91b9e438ed860f1a2b" } } } ``` #### Timeout configuration By default, clients connect, send and receive data with unlimited waiting without timeout, but if the network to access the server is unstable, then there is a chance that access may get stuck, and this can be solved by configuring a timeout. If a timeout exception occurs, it will automatically degrade to local cache and will not be stuck forever. We can also configure the timeout just for the current remote cache service, leaving the other services with the default timeout. ```sh { distcc_build = { send_timeout = 5000, recv_timeout = 5000, connect_timeout = 5000, } } ``` ::: tip NOTE The server-side configuration also supports timeout configuration. ::: ### User authorization For user authorization, please refer to [Remote Compilation/User Authorization](/guide/extras/remote-compilation#user-authorization). ### Connect to the server After configuring the authentication and server address, you can enter the following command to connect the current project to the configured server. We need to enter `--ccache` when connecting to specify that only the remote compilation cache service is connected. ```sh $ cd projectdir $ xmake service --connect --ccache : connect 127.0.0.1:9692 .. : 127.0.0.1:9692 connected! ``` We can also connect to multiple services at the same time, such as distributed compilation and remote compilation cache services. ```sh $ xmake service --connect --distcc --ccache ``` If there is no parameter, the default connection is the remote compilation service. #### Disconnect ```sh $ xmake service --disconnect --ccache ``` #### Clean the server cache We can also use the following command to clear the cache on the remote server corresponding to the current project. ```sh $ xmake service --clean --ccache ``` ...and if we execute `xmake clean --all`, when the remote service is connected, all caches will be automatically cleaned up. #### Some internal optimizations 1. Pull the snapshot of the remote cache and send it back to the local through a bloom filter + lz4, which is used to quickly determine whether the cache exists and avoid frequently querying the server cache information 2. With the local cache, you can avoid frequent requests to the remote server and pull the cache. 3. Internal state maintenance, compared with independent tools such as sscache, avoids frequent independent process loading and time-consuming, and avoids additional communication with the daemon process --- --- url: /guide/basic-commands/build-configuration.md --- # Build Configuration Set compilation configuration before building the project with the command `xmake f|config`. If you want to know more options, please run: `xmake f --help`. ::: tip NOTE You can use short or long command options, for example: `xmake f` or `xmake config`. `xmake f -p linux` or `xmake config --plat=linux`. ::: ## Switch Platforms ### Current Host :::tip NOTE Xmake will detect the current host platform automatically and build the project. ::: ```sh $ xmake ``` ### Linux ```sh $ xmake f -p linux [-a i386|x86_64] $ xmake ``` ### Android ```sh $ xmake f -p android --ndk=~/files/android-ndk-r10e/ [-a armeabi-v7a|arm64-v8a] $ xmake ``` If you want to set other Android toolchains, you can use the [--bin](#-bin) option. For example: ```sh $ 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 the `bin` directory of toolchains. ::: tip NOTE Please try to set the `--arch=` option if it fails to check the compiler. ::: ### iPhoneOS ```sh $ xmake f -p iphoneos [-a armv7|armv7s|arm64|i386|x86_64] $ xmake ``` Since the emulator on the M1 device also supports the arm64 architecture, it is no longer possible to distinguish the emulator from the architecture alone. Therefore, in version 2.6.5, we have added a new parameter to distinguish between simulator targets and device targets. ```sh $ xmake f -p iphoneos --appledev=simulator $ xmake f -p watchos --appledev=simulator $ xmake f -p appletvos --appledev=simulator ``` ### Mac Catalyst We can also specify building Mac Catalyst programs. ```sh $ xmake f --appledev=catalyst ``` ### Windows ```sh $ xmake f -p windows [-a x86|x64] $ xmake ``` ### MinGW In addition to supporting Msys2/MingW and MingW for macOS/Linux, xmake also supports the llvm-mingw toolchain, which can switch to the arm/arm64 architecture for compilation. ```sh $ xmake f -p mingw --sdk=/usr/local/i386-mingw32-4.3.0/ [-a i386|x86_64|arm|arm64] $ xmake ``` ### WASM (WebAssembly) This platform is used to compile WebAssembly programs (the emcc toolchain is used internally). Before switching to this platform, we need to enter the Emscripten toolchain environment to ensure that emcc and other compilers are available. ```sh $ xmake f -p wasm $ xmake ``` Xmake also supports Qt for wasm compilation. You only need: ```sh $ xmake f -p wasm [--qt=~/Qt] $ xmake ``` The `--qt` parameter setting is optional; usually, xmake can detect the SDK path of Qt. One thing to note is that there is a correspondence between the versions of Emscripten and the Qt SDK. If the versions do not match, there may be compatibility issues between Qt/Wasm. Regarding the version correspondence, you can see: For more details, please see: In addition to emscripten, there is a common wasi-sdk toolchain for building WASI-based programs, and we just need to switch between toolchains. ```sh $ xmake f -p wasm --toolchain=wasi $ xmake ``` ### Apple WatchOS ```sh $ xmake f -p watchos [-a i386|armv7k] $ xmake ``` ### HarmonyOS Version 2.9.1 adds native toolchain compilation support for the HarmonyOS platform: ```sh $ xmake f -p harmony ``` xmake will automatically detect the default SDK path, but you can also specify the Harmony SDK path. ```sh $ xmake f -p Harmony --sdk=/Users/ruki/Library/Huawei/Sdk/openharmony/10/native ``` ## Global Configuration You can save to the global configuration to simplify operation. For example: ```sh $ xmake g --ndk=~/files/android-ndk-r10e/ ``` Now, we configure and build the project for Android again. ```sh $ xmake f -p android $ xmake ``` ::: tip NOTE You can use short or long command options, for example: `xmake g` or `xmake global`. ::: ## Clean Configuration We can clean all cached configuration and re-configure the project. ```sh $ xmake f -c $ xmake ``` or ```sh $ xmake f -p iphoneos -c $ xmake ``` ## Import and export configuration After 2.5.5, we can also import and export the configuration set to facilitate rapid configuration migration. ### Export configuration ```sh $ xmake f --export=/tmp/config.txt $ xmake f -m debug --xxx=y --export=/tmp/config.txt ``` ### Import configuration ```sh $ xmake f --import=/tmp/config.txt $ xmake f -m debug --xxx=y --import=/tmp/config.txt ``` ### Export configuration (with menu) ```sh $ xmake f --menu --export=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --export=/tmp/config.txt ``` ### Import configuration (with menu) ```sh $ xmake f --menu --import=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --import=/tmp/config.txt ``` --- --- url: /guide/basic-commands/build-targets.md --- # Build Targets {#build-targets} We briefly mentioned before that we can use the `xmake build` command to build a project. Here we will explain it in detail. First, let's take a look at its complete command format. ## Command format ```sh $ xmake build [options] [target] ``` Here, `[target]` specifies the target to be built. This is optional. If it is not set, all target programs will be built by default (except those marked as default = false). The execution results are as follows: ```sh $ xmake build [ 17%]: cache compiling.release src/main.cpp [ 23%]: cache compiling.release src/foo.cpp [ 35%]: linking.release libfoo.a [ 71%]: linking.release test [100%]: build ok, spent 1.173s ``` Usually, we can omit the `build` subcommand, because the default behavior of the xmake command is to perform the build. ```sh $ xmake [ 17%]: cache compiling.release src/main.cpp [ 23%]: cache compiling.release src/foo.cpp [ 35%]: linking.release libfoo.a [ 71%]: linking.release test [100%]: build ok, spent 1.173s ``` ## Build a specific target If you want to build a specific target program, you can run: ```sh $ xmake build foo ``` At this time, you need to write the full build subcommand, otherwise the target name may conflict with other subcommands. ## Rebuild the target ```sh $ xmake -r ``` Or ```sh $ xmake --rebuild ``` Both can achieve forced recompilation of the target program. ## Build all target programs If a target is configured as `default = false`, it will not be compiled by default. ```lua target("test") set_default(false) add_files("src/*.c") ``` If you want to build all targets, including those with `default = false`, you can pass the `-a/--all` parameter. ```sh $ xmake build -a ``` Or ```sh $ xmake build --all ``` ## Find detailed compilation commands If we want to view the complete compiler command parameters to troubleshoot flag configuration and other issues, we can use `xmake -v`. ```sh $ xmake -v [23%]: cache compiling.release src/main.cpp /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.2 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp [47%]: linking.release test /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -o build/macosx/x86_64/release/test build/.objs/test/macosx/x86_64/release/src/main.cpp.o -target x86_64-apple-macos15.2 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -lz -Wl,-x -Wl,-dead_strip [100%]: build ok, spent 0.585s ``` ## View error log and stack If you encounter a problem during the compilation process and the build fails, the compilation error will be displayed by default, but if there is a syntax error in the configuration, the complete stack will not be displayed. If you want to further locate the problem, you can use the following command to view the complete build log, including the configuration stack. ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.cpp") target("test") set_kind("binary") dd_deps("foo") --------------- Incorrect interface add_files("src/main.cpp") ``` ```sh $ xmake -vD error: @programdir/core/main.lua:329: @programdir/core/sandbox/modules/import/core/base/task.lua:65: @progr amdir/core/project/project.lua:1050: ./xmake.lua:9: attempt to call a nil value (global 'dd_deps') stack traceback: [./xmake.lua:9]: in main chunk ----------------- here stack traceback: [C]: in function 'error' @programdir/core/base/os.lua:1075: in function 'os.raiselevel' (...tail calls...) @programdir/core/main.lua:329: in upvalue 'cotask' @programdir/core/base/scheduler.lua:406: in function <@programdir/core/base/scheduler.lua:399> ``` --- --- url: /api/description/builtin-policies.md --- # Built-in Policies Xmake incorporates many default behaviors, such as automatically detecting and mapping for flags and enabling parallel builds across different targets. Although it provides intelligent processing to some extent, it is difficult to satisfy all users' habits, needs and preferences. Therefore, xmake provides a way to override its default build policies, giving users a higher degree of control. This is primarily achieved through the [set\_policy](/api/description/project-target#set-policy) API, which can be used to modify the default behavior of targets, packages, or the entire project. ## Usage ::: tip NOTE If the policy name you provide is invalid, xmake will show a warning. ::: ### 1. Get All the Policies Supported by the Current Version We can run the following command to get a list of all the policy configurations, including their descriptions, types, and default values: ```sh $ xmake l core.project.policy.policies ``` ### 2. Configuring Policies in `xmake.lua` ::: code-group ```lua [globally] -- Set this in the root domain to globally disable the automatic detection and ignore mechanism of flags set_policy("check.auto_ignore_flags", false) ``` ```lua [locally] target ("test") -- This applies only to the `test` target.     set_policy("check.auto_ignore_flags", false) ``` ::: ### 3. Configuring Policies via the Command Line When building projects, we may need to temporarily enable or disable certain policies. In such cases, it's more suitable to use the command line. And when a policy is set from the command line, it is enabled by default: ```sh $ xmake f --policies=package.fetch_only ``` Also, we can specify other values to disable the policy or achieve other effects: ```sh $ xmake f --policies=package.precompiled:n ``` Please note that when specifying multiple policies, you should use commas to separate them: ```sh $ xmake f --policies=package.precompiled:n,package.install_only ``` ## check.auto\_ignore\_flags ::: danger Potential Risk Disabling the default policy for auto-detecting and ignoring flags without careful consideration may cause unexpected compiling errors for others, as compilers' support for specific flags varies. ::: ### Default policy By default, xmake will automatically detect all the compilation and linking flags set by the `add_cxflags` and `add_ldflags` interfaces. If the current compiler or linker does not support a certain flag, xmake will automatically ignore it and show a warning. This can be extremely useful especially when dealing with optional compilation flags. It can still be compiled normally even if the flag in question is not supported. ### Use Cases However, in some circumstances, this default policy can result in false positives, especially during cross-compilation. What's more, the resulting warnings can be annoying and create a lot of noise in the build output. For example: ``` warning: add_ldflags("-static") is ignored, please pass `{force = true}` or call `set_policy("check.auto_ignore_flags", false)` if you want to set it. ``` Based on this warning, we can determine if we need to add this flag. Depending on the number of flags involved, you can choose one of the following two solutions: #### 1. Add the `force` parameter ```lua add_ldflags("-static", {force = true}) ``` The `{force = true}` parameter can explicitly force the flag(s) to be added, skipping the automatic check. This is an effective and fast way to deal with a few flags. However, when dealing with complex projects like cross-compilation, where a large number of flags might fail the detection, setting this parameter for each one becomes tedious. #### 2. Set the Policy To avoid the limitation of the first approach, we can use `set_policy` to directly disable the default automatic detection behavior for a specified target or the entire project. Meanwhile, this will suppress all related warnings. ```lua set_policy("check.auto_ignore_flags", false) target("test") add_ldflags("-static") ``` ## check.auto\_map\_flags ::: tip NOTE The current implementation of auto-mapping is not yet complete and doesn't cover 100% of all gcc flags, so there are still some flags that may not be mapped. ::: ### Default Policy This policy controls another intelligent feature for flag-handling. Usually, the configurations set by xmake built-in APIs like `add_links`, `add_defines` are cross-platform, which means xmake will automatically translate them into appropriate raw flags for different compilers. However, in some cases, users still need to set the raw compilation or linking flags by APIs like `add_cxflags` and `add_ldflags`. These APIs takes raw flags as parameters, and therefore are probably compiler-specified and not portable. Take `-O0`, a compiler optimization flag, for example. Although `set_optimize` can be used to implement cross-platform configuration, some users still prefer to use `add_cxflags("-O0")`. `-O0` can be recognized by gcc and clang, but not by msvc. To solve this, xmake has a built-in auto-mapping function for flags. Based on the widely-used gcc flags, xmake uses gcc's flags naming convention to automatically map them. For example: ```lua add_cxflags("-O0") ``` Xmake will pass `-O0` to gcc and clang as it is, but for msvc, it will be automatically mapped to msvc-specified `-Od` flag to disable optimization. During this transparent process, simply run xmake, and it will handle the necessary flag translations automatically. ### Usage Some users do not like this auto-mapping behavior, so we can completely disable this default behavior through the following settings: ```lua set_policy("check.auto_map_flags", false) ``` ## check.target\_package\_licenses ### Default Policy Using open-source software as third-party dependencies can avoid "reinventing the wheel" in software development. We should **respect and value** other developers' work by following their licenses. In order to help developers find potential risks, xmake performs a **license compatibility check** on projects and its imported dependencies by default. ### Usage This policy is intended to help developers identify potential license compliance issues. But in some learning or experimental scenarios, you may want to temporarily silence the conflict warnings generated by this check. To do so, you can configure your project like this: ```lua set_policy("check.target_package_licenses", false) ``` ## build.across\_targets\_in\_parallel This strategy is also enabled by default and is mainly used to perform parallel builds between targets. In versions prior to v2.3.3, parallel builds can only target all source files within a single target. For cross-target compilation, you must wait until the previous target is fully linked before you can execute the compilation of the next target, which will affect the compilation speed to a certain extent. However, the source files of each target can be completely parallelized, and finally the link process is executed together. Versions after v2.3.3 through this optimization, the construction speed is increased by 30%. Of course, if the build source files in some special targets depend on previous targets (especially in the case of some custom rules, although rarely encountered), we can also disable this optimization behavior through the following settings: ```sh set_policy("build.across_targets_in_parallel", false) ``` ## build.fence Due to the limitation of `set_policy(‘build.across_targets_in_parallel’, false)`, it will limit the parallelism between the parent target and all of its dependent subtargets, which is a bit wide. When we do codegen, sometimes we just want to limit the parallelism of one of the dependent targets, as a codegen program, and let it finish compiling in advance. In this case, `build.cross_targets_in_parallel` can't control it finely, and the compilation speed can't be optimised. Therefore, we have added the `build.fence` policy, which restricts parallel compilation links to only certain sub-targets. For background, see [#5003](https://github.com/xmake-io/xmake/issues/5003). Example: ```lua target(‘autogen’) set_default(false) set_kind(‘binary’) set_plat(os.host()) set_arch(os.arch()) add_files(‘src/autogen.cpp’) set_languages(‘c++11’) set_policy(‘build.fence’, true) target(‘test’) set_kind(‘binary’) add_deps(‘autogen’) add_rules(‘autogen’) add_files(‘src/main.cpp’) add_files(‘src/*.in’) ``` The autogen target needs to be compiled and linked before the source code of the test program is compiled, because the test target needs to run the autogen program to dynamically generate some source code to participate in the compilation. The autogen configuration `set_policy(‘build.fence’, true)` does this. ## build.merge\_archive If this policy is set, then the target libraries that are dependent on using `add_deps()` no longer exist as links, but are merged directly into the parent target library. Example. ```lua add_rules("mode.debug", "mode.release") target("add") set_kind("static") add_files("src/add.c") add_files("src/subdir/add.c") target("sub") set_kind("static") add_files("src/sub.c") add_files("src/subdir/sub.c") target("mul") set_kind("static") add_deps("add", "sub") add_files("src/mul.c") set_policy("build.merge_archive", true) target("test") add_deps("mul") add_files("src/main.c") ``` The libmul.a static library automatically merges the libadd.a and libsub.a sub-dependent static libraries. ## build.ccache Xmake has a built-in build cache enabled by default, which can be explicitly disabled by setting this policy. ```lua set_policy("build.ccache", false) ``` Of course, we can also disable it on the command line. ```sh $ xmake f --ccache=n ``` or ```sh $ xmake f --policies=build.ccache:n ``` ## build.warning The default compilation usually does not echo the warning output in real time, we usually need to use `xmake -w` to turn it on, or to turn it on globally with `xmake g --build_warning=y`. Now, we can also enable warning echo output by default in the xmake.lua configuration. ```lua set_policy("build.warning", true) set_warnings("all", "extra") ``` At this time, even if we execute the `xmake` command, the warning output can be echoed directly. ## build.optimization.lto xmake v2.6.9 has improved support for link-time optimisation (LTO), with adaptations for different platforms such as gcc/clang/msvc, simply by enabling this policy to enable LTO for specific targets. ```lua set_policy("build.optimization.lto", true) ``` We can also turn it on quickly via the command line option. ```sh $ xmake f --policies=build.optimization.lto ``` ## build.cuda.devlink Version 2.7.7 can be configured to show that device links to specific targets are turned on. This is typically used for Cuda project builds, and for non-Cuda binary/shared dependencies on Cuda static targets, where the Cuda static target needs to show this configuration to turn on device linking. ```lua target("test") set_kind("static") set_policy("build.cuda.devlink", true) ``` Whereas by default Cuda binary/shared is devlink enabled, we can also disable it via the policy display. For a detailed background on this, see: [#1976](https://github.com/xmake-io/xmake/issues/1976) ## build.distcc.remote\_only This policy is used to configure the behavior of distributed compilation. By default, when distributed compilation is enabled, the local machine acts as both a scheduler and participates in the actual compilation work. If this policy is enabled, it forces only remote machines to execute distributed compilation tasks, while the local machine does not participate in actual compilation and only serves as a scheduler. This is very useful when local machine resources are limited, or when you want to offload all compilation work to a remote server cluster. ```lua set_policy("build.distcc.remote_only", true) ``` Or enable it via command line: ```sh $ xmake f --policies=build.distcc.remote_only ``` For more details about distributed compilation, please refer to: [Distributed Compilation](/guide/extras/distributed-compilation) ## build.sanitizer.address Address Sanitizer (ASan) is a fast memory error detection tool that is built-in by the compiler, and usually requires `-fsanitize-address` to be configured in both the build and link flags to enable it correctly. We can quickly enable it globally by turning on this policy, which will result in compiled programs that directly support ASan detection. For example, we can enable it from the command line: ```sh $ xmake f --policies=build.sanitizer.address ``` It can also be enabled globally via the interface configuration: ```lua set_policy("build.sanitizer.address", true) ``` Of course, we can also enable it for a specific target individually. Also, if we configure it globally, we can enable it for all dependencies at the same time. ```lua set_policy("build.sanitizer.address", true) add_requires("zlib") add_requires("libpng") ``` It is equivalent to setting the asan configuration for each package in turn. ```lua add_requires("zlib", {configs = {asan = true}}) add_requires("libpng", {configs = {asan = true}}) ``` ::: tip NOTE `add_rules("mode.asan", "mode.tsan", "mode.ubsan", "mode.msan")` will be deprecated, and these new strategies will be used whenever possible, as these build modes cannot take effect on dependent packages simultaneously. ::: Alternatively, we can validate multiple sanitizer inspections at the same time, e.g.: ```lua set_policy("build.sanitizer.address", true) set_policy("build.sanitizer.undefined", true) ``` or ```sh $ xmake f --policies=build.sanitizer.address,build.sanitizer.undefined ``` ## build.sanitizer.thread Similar to [build.sanitizer.address](#build-sanitizer-address) for detecting thread safety issues. ## build.sanitizer.memory Similar to [build.sanitizer.address](#build-sanitizer-address) for detecting memory issues. ## build.sanitizer.leak Similar to [build.sanitizer.address](#build-sanitizer-address) for detecting memory leaks. ## build.sanitizer.undefined Similar to [build.sanitizer.address](#build-sanitizer-address) for detecting undefined issues. ## build.always\_update\_configfiles This policy is used for the automatic generation of `add_configfiles` configuration files. By default, xmake only triggers the regeneration of configfiles the first time `xmake config` is done, or if the xmake.lua configuration is changed. Each subsequent build will not regenerate configfiles as long as the configuration has not changed. However, if we use a variable such as GIT\_COMMIT in our configfiles and want to always regenerate the latest configuration for each build, we can configure it. For background on how to use it, see: [#4747](https://github.com/xmake-io/xmake/issues/4747) ## build.intermediate\_directory Configures whether to enable or disable internal subdirectories of the build. By default, executing `xmake` to compile the project will automatically generate subdirectories in the build directory according to the platform, architecture, and compilation mode to store object files and target files respectively. For example: ```sh build/ └── macosx └── x86_64 └── release └─test ``` If this policy is disabled, the generated product will be directly generated in the build root directory. Become: ```sh build/ └─ test ``` ## build.rpath Configures to enable or disable the target rpath setting during build. By default, if `target(foo)` depends on the dynamic library bar, the generated foo executable file will automatically add bar's rpath, which ensures that users can directly execute the foo program and find bar correctly. If you want to disable this behavior, you can explicitly configure it. ## install.rpath Although the rpath will be set for the built program, the rpath when it is built may not be completely applicable after `xmake install` is installed, so xmake will automatically modify and adjust the rpath so that the installed program can also find its dependent libraries. However, the premise is that the user must first configure an independent installation rpath through `add_rpathdirs("/xxx", {installonly = true})`. And we can also use this policy to disable the default installation phase rpath setting behavior. ## run.autobuild This policy is used to adjust the behaviour of `xmake run`. By default, running `xmake run` does not build the target program automatically, but prompts the user to build it manually if it has not been compiled yet. By turning on this policy, we can automatically build the target program before running it. ```sh $ xmake f --policies=run.autobuild $ xmake run ``` If you want this policy to take effect globally, you can turn it on globally. ```sh $ xmake g --policies=run.autobuild ``` ## preprocessor.linemarkers If this policy is turned off, then the cache will generate preprocessor files without linemarkers, which will greatly reduce the size of the preprocessor files. This will greatly reduce the size of the preprocessor file and improve the efficiency of the cache, but the downside is that the source line information will be lost and if you encounter a compilation error, you will not be able to see the exact line of code that went wrong. ## preprocessor.gcc.directives\_only This is also used as a preprocessor policy and is enabled by default. This will improve the efficiency of compile cache preprocessing under gcc, but can lead to cache inconsistencies if the source file contains macros such as `__DATE__`, `__TIME__`, etc. Therefore, you can turn this policy off as needed to ensure consistent results, depending on your project code. ## package.requires\_lock Can be used to enable version locking of dependency packages introduced by `add_requires()`. See [Dependent package lock and upgrade](/guide/package-management/using-official-packages#dependent-package-lock-and-upgrade). ## package.precompiled Can be used to disable fetching of precompiled dependency packages under windows. ## package.fetch\_only If this policy is enabled, then all dependencies will only be fetched from the system and not downloaded and installed from a remote location. ## package.install\_only If this policy is enabled, then all dependencies will only be downloaded and installed remotely, not fetched from the system. ## package.librarydeps.strict\_compatibility Disabled by default, if enabled then strict compatibility is maintained between the current package and all its library dependencies, any version update of a dependent package will force a recompile install of the current package. This ensures that all packages are binary compatible and that no linking and runtime errors occur when linking with other installed packages due to changes to the interface of a dependent package. ```lua package("foo") add_deps("bar", "zoo") set_policy("package.librarydeps.strict_compatibility", true) ``` For example, if there is an updated version of bar or zoo, then foo will also be recompiled and installed. ## package.strict\_compatibility is disabled by default, if it is enabled then strict compatibility is maintained between the current package and all other packages that depend on it, and any version update of this package will force a recompile and install of the other parent packages. This ensures that all packages are binary compatible and that no linking and runtime errors occur when linking with other installed packages due to changes in the interface of a dependent package. ```lua package("foo") set_policy("package.strict_compatibility", true) package("bar") add_deps("foo") package("zoo") add_deps("foo") ``` For example, if there is an updated version of foo, then both bar and zoo will be forced to be recompiled and installed. ## package.install\_always This is useful for local integration of third-party source packages, as the package will always be reinstalled each time `xmake f -c` is run to reconfigure it. As the user may at any time need to modify the third party source code and recompile it for integration. Previously it was only possible to trigger a recompile by changing the package version number each time, but with this strategy it is possible to trigger a recompile each time. ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) set_policy("package.install_always", true) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" . (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" ... (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` ## package.download.http\_headers Setting http headers for package downloads If some packages have url downloads that require specific http headers to be set in order to pass the download, this policy can be specified. ```lua package("xxx") set_policy("package.download.http_headers", "TEST1: foo", "TEST2: bar") ``` We can also set the http headers for the specified urls: ```lua add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", { http_headers = {"TEST1: foo", "TEST2: bar"} }) ``` ## windows.manifest.uac This policy allows us to quickly and easily setup and enable Windows UAC. It supports the following levels: | Level | Flag | | --- | --- | | invoker | asInvoker | | admin | requireAdministrator | | highest | highestAvailable | Example: ```lua set_policy("windows.manifest.uac", "admin") ``` It is equivalent to setting ```lua if is_plat("windows") then add_ldflags("/manifest:embed", {"/manifestuac:level='requireAdministrator' uiAccess='false'"}, {force = true, expand = false}) end ``` But it's easier and cleaner, and doesn't need to judge the platform, other platforms are automatically ignored. ## windows.manifest.uac.ui Sets uiAccess for Windows UAC, defaults to false if it is not set. ```lua set_policy("windows.manifest.uac.ui", true) ``` --- --- url: /guide/extensions/builtin-plugins.md --- # Builtin Plugins ## Generate IDE Project Files ### Generate Makefile ```sh $ xmake project -k makefile ``` ### Generate CMakelists.txt ```sh $ xmake project -k cmakelists ``` ### Generate build.ninja ```sh $ xmake project -k ninja ``` ### Generate compile\_flags ```sh $ xmake project -k compile_flags ``` ### Generate compile\_commands We can export the compilation commands info of all source files and it is JSON compilation database format. ```sh $ xmake project -k compile_commands ``` 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 you need to know more about `compile_commands`. ### Generate Xcode project file The current historical version uses CMake to generate Xcode projects, but the latest dev version, which is the upcoming 3.0.1 version, will bring a native Xcode generator. If you want to experience it in advance, you can update to the xmake dev version and try it: `xmake update -s dev`. For details, see: [#4810](https://github.com/xmake-io/xmake/issues/4810). ```sh $ xmake project -k xcode ``` ### Generate VisualStudio Project #### Compile with xmake integration v2.2.8 or later, provides a new version of the vs project generation plugin extension, which is very different from the previous plugin processing mode for generating vs. The previously generated vs project is the compilation of all files and then transferred to vs. To handle compilation. But in this mode, there is no way to support the rules of xmake. Because xmake's rules use a lot of custom scripts like `on_build`, they can't be expanded, so projects like qt, wdk can't support exporting to vs. compile. Therefore, in order to solve this problem, the new version of the vs. build plugin performs the compile operation by directly calling the xmake command under vs, and also supports intellisense, definition jumps, as well as breakpoint debugging. But the premise is that the user has installed the corresponding toolchain environment on the system. The specific use is similar to the old version: ```sh $ xmake project -k [vsxmake2010|vsxmake2013|vsxmake2015|..] -m "debug;release" ``` If no version is specified, xmake will automatically detect the current version of VS to generate: ```sh $ xmake project -k vsxmake -m "debug,release" ``` ![](/assets/img/manual/qt_vs.png) In addition, the vsxmake plugin will additionally generate a custom configuration property page for easy and flexible modification and appending some xmake compilation configuration in VS, and even switch to other cross toolchains in the configuration to achieve cross-compilation of other platforms such as Android, Linux. ![](/assets/img/manual/property_page_vsxmake.png) The v2.5.1 version provides a `add_rules("plugin.vsxmake.autoupdate")` rule. If this rule is applied, the generated VS project will be checked for changes in xmake.lua and the code file list after the compilation is completed. If there are changes, the VS project will be updated automatically. ```lua add_rules("plugin.vsxmake.autoupdate") target("test") set_kind("binary") add_files("src/*.c") ``` In addition, we can group each target through the `set_group` interface, so that the generated VS project can be grouped according to the specified structure. For more details, please see: [issue 1026](https://github.com/xmake-io/xmake/issues/1026) #### Using vs built-in compilation mechanism ::: tip NOTE It is recommended to use the new version of the vs. plugin provided after v2.2.8 mentioned above. The support is more complete. The generation method here does not support the rules of xmake, and the generation of projects such as qt. ::: ```sh $ xmake project -k [vs2008|vs2013|vs2015|..] ``` v2.1.2 or later, it supports multi-mode and multi-architecture generation for vs201x project. For example: ```sh $ 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 in `xmake.lua`: ```lua set_modes("debug", "release") ``` Then, we run the following command: ```sh $ xmake project -k vs2017 ``` The effect is the same. In addition, we can group each target through the `set_group` interface, so that the generated VS project can be grouped according to the specified structure. For more details, please see: [issue 1026](https://github.com/xmake-io/xmake/issues/1026) ## 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. ```sh $ xmake lua /tmp/test.lua ``` ::: tip NOTE You can also use the `import` API to write a more advanced Lua script. ::: ### Run the builtin script You can run `xmake lua -l` to list all builtin script name, for example: ```sh $ xmake lua -l scripts: cat cp echo versioninfo ... ``` And run them: ```sh $ 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: ```sh $ 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: ```sh > task = import("core.project.task") > task.run("hello") hello xmake! ``` If you want to cancel multiline input, please input character `q`, for example: ```sh > for _, v in ipairs({1, 2}) do >> print(v) >> q <-- cancel multiline and clear previous input > 1 + 2 3 ``` ## Show specified information and list ### Show basic information about xmake itself and the current project ```sh $ xmake show The information of xmake: version: 2.3.3+202006011009 host: macosx/x86_64 programdir: /Users/ruki/.local/share/xmake programfile: /Users/ruki/.local/bin/xmake globaldir: /Users/ruki/.xmake tmpdir: /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/200603 workingdir: /Users/ruki/projects/personal/tbox packagedir: /Users/ruki/.xmake/packages packagedir(cache): /Users/ruki/.xmake/cache/packages/2006 The information of project: tbox version: 1.6.5 plat: macosx arch: x86_64 mode: release buildir: build configdir: /Users/ruki/projects/personal/tbox/.xmake/macosx/x86_64 projectdir: /Users/ruki/projects/personal/tbox projectfile: /Users/ruki/projects/personal/tbox/xmake.lua ``` ### Show toolchains list ```sh $ xmake show -l toolchains xcode Xcode IDE vs VisualStudio IDE yasm The Yasm Modular Assembler clang A C language family frontend for LLVM go Go Programming Language Compiler dlang D Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit ndk Android NDK rust Rust Programming Language Compiler llvm A collection of modular and reusable compiler and toolchain technologies cross Common cross compilation toolchain nasm NASM Assembler gcc GNU Compiler Collection mingw Minimalist GNU for Windows gnu-rm GNU Arm Embedded Toolchain envs Environment variables toolchain fasm Flat Assembler ``` ### Show the information of the given target We can use it to quickly trace the location of some specific configurations. ```sh $ xmake show -t tbox The information of target(tbox): at: /Users/ruki/projects/personal/tbox/src/tbox/xmake.lua kind: static targetfile: build/macosx/x86_64/release/libtbox.a rules: -> mode.release -> ./xmake.lua:26 -> mode.debug -> ./xmake.lua:26 -> mode.profile -> ./xmake.lua:26 -> mode.coverage -> ./xmake.lua:26 -> utils.install.cmake_importfiles -> ./src/tbox/xmake.lua:15 -> utils.install.pkgconfig_importfiles -> ./src/tbox/xmake.lua:16 options: -> info -> ./src/tbox/xmake.lua:50 -> float -> ./src/tbox/xmake.lua:50 -> wchar -> ./src/tbox/xmake.lua:50 -> exception -> ./src/tbox/xmake.lua:50 -> force-utf8 -> ./src/tbox/xmake.lua:50 -> deprecated -> ./src/tbox/xmake.lua:50 -> xml -> ./src/tbox/xmake.lua:53 -> zip -> ./src/tbox/xmake.lua:53 -> hash -> ./src/tbox/xmake.lua:53 -> regex -> ./src/tbox/xmake.lua:53 -> coroutine -> ./src/tbox/xmake.lua:53 -> object -> ./src/tbox/xmake.lua:53 -> charset -> ./src/tbox/xmake.lua:53 -> database -> ./src/tbox/xmake.lua:53 packages: -> mbedtls -> ./src/tbox/xmake.lua:43 -> polarssl -> ./src/tbox/xmake.lua:43 -> openssl -> ./src/tbox/xmake.lua:43 -> pcre2 -> ./src/tbox/xmake.lua:43 -> pcre -> ./src/tbox/xmake.lua:43 -> zlib -> ./src/tbox/xmake.lua:43 -> mysql -> ./src/tbox/xmake.lua:43 -> sqlite3 -> ./src/tbox/xmake.lua:43 links: -> pthread -> option(__keyword_thread_local) -> @programdir/includes/check_csnippets.lua:100 syslinks: -> pthread -> ./xmake.lua:71 -> dl -> ./xmake.lua:71 -> m -> ./xmake.lua:71 -> c -> ./xmake.lua:71 defines: -> __tb_small__ -> ./xmake.lua:42 -> __tb_prefix__="tbox" -> ./src/tbox/xmake.lua:19 -> _GNU_SOURCE=1 -> option(__systemv_semget) -> @programdir/includes/check_cfuncs.lua:104 cxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:22 -> -fno-strict-aliasing -> ./xmake.lua:22 -> -Wno-error=expansion-to-defined -> ./xmake.lua:22 -> -fno-stack-protector -> ./xmake.lua:51 frameworks: -> CoreFoundation -> ./src/tbox/xmake.lua:38 -> CoreServices -> ./src/tbox/xmake.lua:38 mxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:23 -> -fno-strict-aliasing -> ./xmake.lua:23 -> -Wno-error=expansion-to-defined -> ./xmake.lua:23 includedirs: -> src -> ./src/tbox/xmake.lua:26 -> build/macosx/x86_64/release -> ./src/tbox/xmake.lua:27 headerfiles: -> src/(tbox/**.h)|**/impl/**.h -> ./src/tbox/xmake.lua:30 -> src/(tbox/prefix/**/prefix.S) -> ./src/tbox/xmake.lua:31 -> src/(tbox/math/impl/*.h) -> ./src/tbox/xmake.lua:32 -> src/(tbox/utils/impl/*.h) -> ./src/tbox/xmake.lua:33 -> build/macosx/x86_64/release/tbox.config.h -> ./src/tbox/xmake.lua:34 files: -> src/tbox/*.c -> ./src/tbox/xmake.lua:56 -> src/tbox/hash/bkdr.c -> ./src/tbox/xmake.lua:57 -> src/tbox/hash/fnv32.c -> ./src/tbox/xmake.lua:57 -> src/tbox/hash/adler32.c -> ./src/tbox/xmake.lua:57 -> src/tbox/math/**.c -> ./src/tbox/xmake.lua:58 -> src/tbox/libc/**.c|string/impl/**.c -> ./src/tbox/xmake.lua:59 -> src/tbox/utils/*.c|option.c -> ./src/tbox/xmake.lua:60 -> src/tbox/prefix/**.c -> ./src/tbox/xmake.lua:61 -> src/tbox/memory/**.c -> ./src/tbox/xmake.lua:62 -> src/tbox/string/**.c -> ./src/tbox/xmake.lua:63 -> src/tbox/stream/**.c|**/charset.c|**/zip.c -> ./src/tbox/xmake.lua:64 -> src/tbox/network/**.c|impl/ssl/*.c -> ./src/tbox/xmake.lua:65 -> src/tbox/algorithm/**.c -> ./src/tbox/xmake.lua:66 -> src/tbox/container/**.c|element/obj.c -> ./src/tbox/xmake.lua:67 -> src/tbox/libm/impl/libm.c -> ./src/tbox/xmake.lua:68 -> src/tbox/libm/idivi8.c -> ./src/tbox/xmake.lua:73 -> src/tbox/libm/ilog2i.c -> ./src/tbox/xmake.lua:70 -> src/tbox/libm/isqrti.c -> ./src/tbox/xmake.lua:71 -> src/tbox/libm/isqrti64.c -> ./src/tbox/xmake.lua:72 -> src/tbox/platform/*.c|context.c|exception.c -> ./src/tbox/xmake.lua:74 -> src/tbox/platform/impl/*.c|charset.c|poller_fwatcher.c -> ./src/tbox/xmake.lua:74 -> src/tbox/libm/*.c -> ./src/tbox/xmake.lua:77 compiler (cc): /usr/bin/xcrun -sdk macosx clang -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk linker (ar): /usr/bin/xcrun -sdk macosx ar -> -cr compflags (cc): -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -Wall -Werror -Oz -std=c99 -Isrc -Ibuild/macosx/x86_64/release -D__tb_small__ -D__tb_prefix__=\"tbox\" -D_GNU_SOURCE=1 -framework CoreFoundation -framework CoreServices -Wno-error=deprecated-declarations -fno-strict-aliasing -Wno-error=expansion-to-defined -fno-stack-protector linkflags (ar): -> -cr ``` ### Show builtin compilation modes list ```sh $ xmake show -l buildmodes ``` ### Show builtin compilation rules list ```sh $ xmake show -l rules ``` ### Show other information It is still being perfected, see: https://github.com/xmake-io/xmake/issues/798 Or run ```sh $ xmake show --help ``` ## Watching for file updates New in v2.7.1 is the `xmake watch` plugin command, which can automatically monitor project files for updates and then trigger an automatic build or run some custom commands. This is often used for personal development to enable fast, real-time incremental builds without the need to manually execute the build command each time, improving development efficiency. ### Build automatically after a project update The default behaviour is to monitor the entire project root directory and any file changes will trigger an incremental build of the project. ````sh $ xmake watch watching /private/tmp/test/src/** . watching /private/tmp/test/* ... /private/tmp/test/src/main.cpp modified [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test [ 100%]: build ok! ```'' ### Monitoring a specific directory We can also monitor specific code directories to narrow down the scope of monitoring and improve performance. ```sh $ xmake watch -d src $ xmake watch -d "src;tests/*" ```` The above command will recursively watch all subdirectories. If you want to keep a tight watch on the files in the current directory and not do recursive monitoring, you can use the following command. ```sh $ xmake watch -p src $ xmake watch -p "src;tests/*" ``` ### Watch and run the specified command If you want to run the build automatically even after the automatic build, we can use a custom command set. ```sh $ xmake watch -c "xmake; xmake run" ``` The above list of commands is passed as a string, which is not flexible enough for complex command arguments that need to be escaped rather tediously, so we can use the following for arbitrary commands. ```sh $ xmake watch -- echo hello xmake! $ xmake watch -- xmake run --help ``` ### Watching and running the target program Although we can automate the running of the target program with custom commands, we also provide more convenient arguments to achieve this behaviour. ```sh $ xmake watch -r $ xmake watch --run [100%]: build ok! hello world! ``` ### Watching and running lua scripts We can also watch for file updates and then run the specified lua script for more flexible and complex command customisation. ```sh $ xmake watch -s /tmp/test.lua ``` We can also get a list of all updated file paths and events in the script again. ```lua function main(events) -- TODO handle events end ``` ## Check project configurations and codes ### Check project configuration #### Check all api values in xmake.lua by default ```lua set_lanuages("c91") -- typo ``` ```sh $ xmake check ./xmake.lua:15: warning: unknown language value 'c91', it may be 'c90' 0 notes, 1 warnings, 0 errors ``` we can also run a given group ```sh $ xmake check api $ xmake check api.target ``` #### Verbose output ```sh $ xmake check -v ./xmake.lua:15: warning: unknown language value 'cxx91', it may be 'cxx98' ./src/tbox/xmake.lua:43: note: unknown package value 'mbedtls' ./src/tbox/xmake.lua:43: note: unknown package value 'polarssl' ./src/tbox/xmake.lua:43: note: unknown package value 'openssl' ./src/tbox/xmake.lua:43: note: unknown package value 'pcre2' ./src/tbox/xmake.lua:43: note: unknown package value 'pcre' ./src/tbox/xmake.lua:43: note: unknown package value 'zlib' ./src/tbox/xmake.lua:43: note: unknown package value 'mysql' ./src/tbox/xmake.lua:43: note: unknown package value 'sqlite3' 8 notes, 1 warnings, 0 errors ``` #### Check the given api ```sh $ xmake check api.target.languages ./xmake.lua:15: warning: unknown language value 'cxx91', it may be 'cxx98' 0 notes, 1 warnings, 0 errors ``` #### Check compiler flags ```sh $ xmake check ./xmake.lua:10: warning: clang: unknown c compiler flag '-Ox' 0 notes, 1 warnings, 0 errors ``` #### Check includedirs ```sh $ xmake check ./xmake.lua:11: warning: includedir 'xxx' not found 0 notes, 1 warnings, 0 errors ``` ### Check project code (clang-tidy) #### List clang-tidy checks ```sh $ xmake check clang.tidy --list Enabled checks: clang-analyzer-apiModeling.StdCLibraryFunctions clang-analyzer-apiModeling.TrustNonnull clang-analyzer-apiModeling.google.GTest clang-analyzer-apiModeling.llvm.CastValue clang-analyzer-apiModeling.llvm.ReturnValue ... ``` #### Check source code in targets ```sh $ xmake check clang.tidy 1 error generated. Error while processing /private/tmp/test2/src/main.cpp. /tmp/test2/src/main.cpp:1:10: error: 'iostr' file not found [clang-diagnostic-error] #include ^~~~~~~ Found compiler error(s). error: execv(/usr/local/opt/llvm/bin/clang-tidy -p compile_commands.json /private/tmp/test2/src /main.cpp) failed(1) ``` #### Check code with the given checks ```sh $ xmake check clang.tidy --checks="*" 6 warnings and 1 error generated. Error while processing /private/tmp/test2/src/main.cpp. /tmp/test2/src/main.cpp:1:10: error: 'iostr' file not found [clang-diagnostic-error] #include ^~~~~~~ /tmp/test2/src/main.cpp:3:1: warning: do not use namespace using-directives; use using-declarat ions instead [google-build-using-namespace] using namespace std; ^ /tmp/test2/src/main.cpp:3:17: warning: declaration must be declared within the '__llvm_libc' na mespace [llvmlibc-implementation-in-namespace] using namespace std; ^ /tmp/test2/src/main.cpp:5:5: warning: declaration must be declared within the '__llvm_libc' nam espace [llvmlibc-implementation-in-namespace] int main(int argc, char **argv) { ^ /tmp/test2/src/main.cpp:5:5: warning: use a trailing return type for this function [modernize-u se-trailing-return-type] int main(int argc, char **argv) { ~~~ ^ auto -> int /tmp/test2/src/main.cpp:5:14: warning: parameter 'argc' is unused [misc-unused-parameters] int main(int argc, char **argv) { ^~~~ /*argc*/ /tmp/test2/src/main.cpp:5:27: warning: parameter 'argv' is unused [misc-unused-parameters] int main(int argc, char **argv) { ^~~~ /*argv*/ Found compiler error(s). error: execv(/usr/local/opt/llvm/bin/clang-tidy --checks=* -p compile_commands.json /private/tm p/test2/src/main.cpp) failed(1) ``` #### Check code with the given target name ```sh $ xmake check clang.tidy [targetname] ``` #### Check code with the given source files ```sh $ xmake check clang.tidy -f src/main.c $ xmake check clang.tidy -f 'src/*.c:src/**.cpp' ``` #### Set the given .clang-tidy config file ```sh $ xmake check clang.tidy --configfile=/tmp/.clang-tidy ``` #### Create a new .clang-tidy config file ```sh $ xmake check clang.tidy --checks="*" --create $ cat .clang-tidy --- Checks: 'clang-diagnostic-*,clang-analyzer-*,*' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: none User: ruki CheckOptions: - key: readability-suspicious-call-argument.PrefixSimilarAbove value: '30' - key: cppcoreguidelines-no-malloc.Reallocations value: '::realloc' ``` #### Automatically fixing error codes We can use the following command parameters to automatically fix problematic code detected by clang tidy. ```console $ xmake check clang.tidy --fix $ xmake check clang.tidy --fix_errors $ xmake check clang.tidy --fix_notes ``` ## Generate installation package (XPack) ### Introduction This plug-in can help users quickly generate installation packages and source code packages for different platforms. It will generate the following installation package formats: * Windows NSIS binary installation package * Windows Wix binary installation package * runself (shell) self-compile installation package * zip/tar.gz binary package * zip/tar.gz source package * RPM binary installation package * SRPM source code installation package * DEB binary installation package Here is a complete example, we can take a brief look at it first: ```lua set_version("1.0.0") add_rules("mode.debug", "mode.release") includes("@builtin/xpack") target("test") set_kind("binary") add_files("src/*.cpp") xpack("test") set_formats("nsis", "zip", "targz", "runself") set_title("hello") set_author("ruki") set_description("A test installer.") set_homepage("https://xmake.io") set_licensefile("LICENSE.md") add_targets("test") add_installfiles("src/(assets/*.png)", {prefixdir = "images"}) add_sourcefiles("(src/**)") set_iconfile("src/assets/xmake.ico") after_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) after_uninstallcmd(function (package, batchcmds) batchcmds:rmdir(package:installdir("resources")) batchcmds:rmdir(package:installdir("stub")) end) ``` We introduce all configuration interfaces of xpack through `includes("@builtin/xpack")`, including the xpack configuration domain and all its domain interfaces. Then we execute: ```sh $xmakepack ``` All installation packages will be generated. ### Generate NSIS installation package As long as you configure the `set_formats("nsis")` format and then execute the `xmake pack` command, you can generate an installation package in NSIS format. In addition, xmake will also automatically install the tools required to generate NSIS packages to achieve true one-click packaging. ```sh $xmakepack note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> nsis 3.09 please input: y (y/n/m) => install nsis 3.09 .. ok [25%]: compiling.release src\main.cpp [37%]: compiling.release src\main.cpp [50%]: linking.release foo.dll [62%]: linking.release test.exe packing build\xpack\test\test-windows-x64-v1.0.0.exe pack ok ``` `test-windows-x64-v1.0.0.exe` is the installation package we generated. Double-click to run it to install our binary files to the specified directory. ![](/assets/img/manual/nsis_1.png) ![](/assets/img/manual/nsis_2.png) ![](/assets/img/manual/nsis_3.png) #### Add component installation We can also add component installation commands to NSIS. Only when the user selects the specified component, its installation command will be executed. ```lua xpack("test") add_components("LongPath") xpack_component("LongPath") set_default(false) set_title("Enable Long Path") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") on_installcmd(function (component, batchcmds) batchcmds:rawcmd("nsis", [[ ${If} $NoAdmin == "false" ; Enable long path WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ${EndIf}]]) end) ``` In this example, we added an NSIS-specific custom command to support long paths. ![](/assets/img/manual/nsis_4.png) ### Generate self-installation package We can also generate self-compiled installation packages based on shell scripts. We need to configure the runself packaging format, and then add the source files that need to participate in compilation and installation through `add_sourcefiles`. Next, we need to customize the on\_installcmd installation script to configure if we compile the source code package, we can simply call a built-in compilation and installation script file, or directly configure compilation and installation commands such as `make install`. For example: ```lua xpack("test") set_formats("runself") add_sourcefiles("(src/**)") on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install"}) end) ``` Then, we execute the `xmake pack` command to generate a self-installed xxx.gz.run package, which uses gzip compression by default. ```sh $xmakepack packing build/xpack/test/test-macosx-src-v1.0.0.gz.run pack ok ``` We can use sh to load and run it to install our program. ```sh $ sh ./build/xpack/test/test-macosx-src-v1.0.0.gz.run ``` We can also look at a more complete example: ```lua xpack("xmakesrc") set_formats("runself") set_basename("xmake-v$(version)") set_prefixdir("xmake-$(version)") before_package(function (package) import("devel.git") local rootdir = path.join(os.tmpfile(package:basename()) .. ".dir", "repo") if not os.isdir(rootdir) then os.tryrm(rootdir) os.cp(path.directory(os.projectdir()), rootdir) git.clean({repodir = rootdir, force = true, all = true}) git.reset({repodir = rootdir, hard = true}) if os.isfile(path.join(rootdir, ".gitmodules")) then git.submodule.clean({repodir = rootdir, force = true, all = true}) git.submodule.reset({repodir = rootdir, hard = true}) end end local extraconf = {rootdir = rootdir} package:add("sourcefiles", path.join(rootdir, "core/**|src/pdcurses/**|src/luajit/**|src/tbox/tbox/src/demo/**"), extraconf ) package:add("sourcefiles", path.join(rootdir, "xmake/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "*.md"), extraconf) package:add("sourcefiles", path.join(rootdir, "configure"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/*.sh"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/man/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/debian/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/msys/**"), extraconf) end) on_installcmd(function (package, batchcmds) batchcmds:runv("./scripts/get.sh", {"__local__"}) end) ``` It is the installation package configuration script of xmake's own source code, which is more complete. For configuration, please refer to: [xpack.lua](https://github.com/xmake-io/xmake/blob/master/core/xpack.lua) Here, it performs compilation and installation by calling the `./scripts/get.sh` installation script built into the source package. ### Generate source code archive package In addition, we can also configure the `srczip` and `srctargz` formats to generate source code compression packages. It is not a complete installation package and has no installation commands. It is only used for source code package distribution. ```lua xpack("test") set_formats("srczip", "srctargz") add_sourcefiles("(src/**)") ``` ```sh $xmakepack packing build/xpack/test/test-macosx-src-v1.0.0.zip .. packing build/xpack/test/test-macosx-src-v1.0.0.tar.gz .. pack ok ``` ### Generate binary archive package We can also configure `zip` and `targz` to generate binary compressed packages. It will automatically compile all bound target target programs and package all required binary programs and library files into zip/tar.gz format. This is usually used to create a green version of the installation package. There is no automatic installation script inside. Users need to set environment variables such as PATH themselves. ```lua xpack("test") set_formats("zip", "targz") add_installfiles("(src/**)") ``` ```sh $xmakepack packing build/xpack/test/test-macosx-v1.0.0.zip .. packing build/xpack/test/test-macosx-v1.0.0.tar.gz .. pack ok ``` ::: tip NOTE It should be noted that to add binary files to the package, use `add_installfiles` instead of `add_sourcefiles`. ::: We can also use `add_targets` to bind the target target programs and libraries that need to be installed. See the interface description for `add_targets` below for more details. ### Generate SRPM source code installation package It can generate source code installation packages in `.src.rpm` format. We can configure add\_targets to associate the targets that need to be built. In the generated srpm package, it will automatically call `xmake build` and `xmake install` to build and install the package. ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./xmake.lua") add_targets("demo") ``` It will generate a spec file similar to the following, and then automatically call rpmbuild to generate the `.src.rpm` package. ``` Name: test Version: 1.0.0 Release: 1%{?dist} Summary: hello License: Apache-2.0 URL: https://xmake.io Source0: test-linux-src-v1.0.0.tar.gz BuildRequires: xmake BuildRequires: gcc BuildRequires: gcc-c++ %description A test installer. %prep %autosetup -n test-1.0.0 -p1 %build xmake build -y test %install xmake install -o %{buildroot}/%{_exec_prefix} test cd %{buildroot} find . -type f | sed 's!^\./!/!' > %{_builddir}/_installedfiles.txt %check %files -f %{_builddir}/_installedfiles.txt %changelog * Fri Dec 22 2023 ruki - 1.0.0-1 - Update to 1.0.0 ``` We can also customize build and installation scripts through `on_buildcmd` and `on_installcmd`. ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./configure") on_buildcmd(function (package, batchcmds) batchcmds:runv("./configure") batchcmds:runv("make") end) on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install", "PREFIX=%{buildroot}"}) end) ``` ### Generate RPM binary installation package The RPM package will directly generate a compiled binary installation package. xmake will automatically call the `rpmbuild --rebuild` command to build the SRPM package and generate it. In XPack, we only need to configure `set_formats("rpm")` to support rpm package generation, and other configurations are exactly the same as srpm packages. ```lua xpack("test") set_formats("rpm") -- TODO ``` ### Packaging command #### Specify packaging format If we have configured multiple packaging formats using `set_formats` in the configuration file, then `xmake pack` will automatically generate packages for all these formats by default. Of course, we can also use `xmake pack --formats=nsis,targz` to selectively specify which formats of packages currently need to be packaged. #### Modify the package file name We can modify the package name through `set_basename()` in the configuration file, or we can modify it through the command line. ```sh $ xmake pack --basename="foo" packing build/xpack/test/foo.zip .. pack ok ``` #### Specify output directory The default output directory is in the build directory, but we can also modify the output path. ```sh $ xmake pack -o /tmp/output ``` #### Disable automatic build If you are building a binary package such as NSIS, `xmake pack` will automatically compile all bound target files first, and then execute the packaging logic. But if we have already compiled it and don't want to compile it every time, but package it directly, we can disable automatic building through the following parameters. ```sh $ xmake pack --autobuild=n ``` ### Interface description For more descriptions of the XPack packaging interface, see: [XPack Packaging Interface Document](/api/description/xpack-interfaces). ## 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 repeatably. ### Record Commands ```sh # begin to record commands $ xmake macro --begin # run some xmake commands $ xmake f -p android --ndk=/xxx/ndk -a arm64-v8a $ 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 ```sh # playback the previous anonymous macro $ xmake macro . ``` ### Named Macro ```sh $ xmake macro --begin $ ... $ xmake macro --end macroname $ xmake macro macroname ``` ### Import and Export Macro Import the given macro file or directory. ```sh $ xmake macro --import=/xxx/macro.lua macroname $ xmake macro --import=/xxx/macrodir ``` Export the given macro to file or directory. ```sh $ xmake macro --export=/xxx/macro.lua macroname $ xmake macro --export=/xxx/macrodir ``` ### List and Show Macro List all builtin macros. ```sh $ xmake macro --list ``` Show the given macro script content. ```sh $ 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 arm64-v8a") 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. ```sh $ xmake macro --import=/xxx/macro.lua [macroname] ``` Playback this macro script. ```sh $ 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. ```sh $ 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 "", (option.get("verbose") and "-v" or "")) -- package it if args.outputdir then os.exec("xmake p -o %s %s", args.outputdir, (option.get("verbose") and "-v" or "")) else os.exec("xmake p %s", (option.get("verbose") and "-v" or "")) 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 ``` ## Generate Doxygen Document Please ensure that the doxygen tool has been installed first. ```sh $ xmake doxygen ``` --- --- url: /api/description/builtin-rules.md --- # Builtin 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. We can view the complete list of built-in rules by running the following command: ```sh $ xmake show -l rules ``` ## 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 if is_mode("debug") then set_symbols("debug") 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 if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end ``` We can switch to this compilation mode by `xmake f -m release`. ## mode.releasedbg Add the configuration rules for the releasedbg compilation mode for the current project xmake.lua, for example: ```lua add_rules("mode.releasedbg") ``` ::: tip NOTE Compared with the release mode, this mode will also enable additional debugging symbols, which is usually very useful. ::: Equivalent to: ```lua if is_mode("releasedbg") then set_symbols("debug") set_optimize("fastest") set_strip("all") end ``` We can switch to this compilation mode by `xmake f -m releasedbg`. ## mode.minsizerel Add the configuration rules for the minsizerel compilation mode for the current project xmake.lua, for example: ```lua add_rules("mode.minsizerel") ``` ::: tip NOTE Compared with the release mode, this mode is more inclined to the minimum code compilation optimization, rather than speed priority. ::: 相当于: ```lua if is_mode("minsizerel") then set_symbols("hidden") set_optimize("smallest") set_strip("all") end ``` We can switch to this compilation mode by `xmake f -m minsizerel`. ## 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 if is_mode("check") then set_symbols("debug") set_optimize("none") 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 if is_mode("profile") then set_symbols("debug") 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 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`. ## mode.valgrind This mode provides valgrind memory analysis and detection support. ```lua add_rules("mode.valgrind") ``` We can switch to this compilation mode by: `xmake f -m valgrind`. ## mode.asan This mode provides AddressSanitizer memory analysis and detection support. ```lua add_rules("mode.asan") ``` We can switch to this compilation mode by: `xmake f -m asan`. ## mode.tsan This mode provides ThreadSanitizer memory analysis and detection support. ```lua add_rules("mode.tsan") ``` We can switch to this compilation mode by: `xmake f -m tsan`. ## mode.lsan This mode provides LeakSanitizer memory analysis and detection support. ```lua add_rules("mode.lsan") ``` We can switch to this compilation mode by: `xmake f -m lsan`. ## mode.ubsan This mode provides UndefinedBehaviorSanitizer memory analysis and detection support. ```lua add_rules("mode.ubsan") ``` We can switch to this compilation mode by: `xmake f -m ubsan`. ## qt.static A static library program used to compile and generate Qt environments: ```lua target("test") 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("test") 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("test") add_rules("qt.console") add_files("src/*.cpp") ``` ## qt.quickapp Quick(qml) ui application for compiling and generating Qt environment. ```lua target("test")     add_rules("qt.quickapp")     add_files("src/*.cpp")     add_files("src/qml.qrc") ``` ## qt.quickapp\_static Quick(qml) ui application (statically linked version) for compiling and generating Qt environment. ::: tip NOTE Need to switch to static library version Qt SDK ::: ```lua target("test")     add_rules("qt.quickapp_static")     add_files("src/*.cpp")     add_files("src/qml.qrc") ``` ## qt.widgetapp Used to compile Qt Widgets (ui/moc) applications ```lua target("test")     add_rules("qt.widgetapp")     add_files("src/*.cpp")     add_files("src/mainwindow.ui")     add_files("src/mainwindow.h") -- add meta header files with Q_OBJECT ``` ## qt.widgetapp\_static Used to compile Qt Widgets (ui/moc) applications (static library version) ::: tip NOTE Need to switch to static library version Qt SDK ::: ```lua target("test")     add_rules("qt.widgetapp_static")     add_files("src/*.cpp")     add_files("src/mainwindow.ui")     add_files("src/mainwindow.h") -- add meta header files with Q_OBJECT ``` For more descriptions of Qt, see: [#160](https://github.com/xmake-io/xmake/issues/160) ## xcode.bundle Used to compile and generate ios/macos bundle program ```lua target("test")      add_rules("xcode.bundle")      add_files("src/*.m")      add_files("src/Info.plist") ``` ## xcode.framework Used to compile and generate ios/macos framework program ```lua target("test")      add_rules("xcode.framework")      add_files("src/*.m")      add_files("src/Info.plist") ``` ## xcode.application Used to compile and generate ios/macos applications ```lua target("test")      add_rules("xcode.application")      add_files("src/*.m", "src/**.storyboard", "src/*.xcassets")      add_files("src/Info.plist") ``` ## 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", {rules = "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", {rules = "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", {rules = "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", {rules = "win.sdk.dotnet"}) ``` ## wdk.sdk.dotnet Used to specify certain c++ source files to be compiled as c++.net. ```lua add_files("xmlhelper.cpp", {rules = "win.sdk.dotnet"}) ``` For more information on WDK rules, see: [#159](https://github.com/xmake-io/xmake/issues/159) ## plugin.vsxmake.autoupdate We can use this rule to automatically update the VS project file (when each build is completed) in the VS project generated by `xmake project -k vsxmake`. ```lua add_rules("plugin.vsxmake.autoupdate") target("test") set_kind("binary") add_files("src/*.c") ``` ## plugin.compile\_commands.autoupdate We can also use this rule to automatically update the generated `compile_commands.json` ```lua add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"}) target("test") set_kind("binary") add_files("src/*.c") ``` ## utils.symbols.export\_all Provided in v2.5.2 and above, we can use it to automatically export all dynamic library symbols. Currently, only the symbol export of windows dll target programs is supported, even if there is no export interface through `__declspec(dllexport)` in the code. xmake will also automatically export all c/c++ interface symbols. ```lua add_rules("mode.release", "mode.debug") target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_all") target("test") set_kind("binary") add_deps("foo") add_files("src/main.c") ``` c++ ```lua add_rules("utils.symbols.export_all", {export_classes = true}) ``` Versions from 2.9.5 onwards also support custom filters to filter the symbol names and source file names that need to be exported: ```lua target("bar") set_kind("shared") add_files("src/bar.cpp") add_rules("utils.symbols.export_all", {export_filter = function (symbol, opt) local filepath = opt.sourcefile or opt.objectfile if filepath and filepath:find("bar.cpp", 1, true) and symbol:find("add", 1, true) then print("export: %s at %s", symbol, filepath) return true end end}) ``` Related issue [#1123](https://github.com/xmake-io/xmake/issues/1123) ## utils.symbols.export\_list We can define the list of exported symbols directly in xmake.lua, for example: ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_list", {symbols = { "add", "sub"}}) ``` Alternatively, add a list of exported symbols in the `*.export.txt` file. ```lua target("foo2") set_kind("shared") add_files("src/foo.c") add_files("src/foo.export.txt") add_rules("utils.symbols.export_list") ``` For a complete project example, see: [Export Symbol Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c/shared_library_export_list) ## utils.install.cmake\_importfiles We can use this rule to export the .cmake file when installing the target library file for the library import and search of other cmake projects. ## utils.install.pkgconfig\_importfiles We can use this rule to export the pkgconfig/.pc file when installing the target target library file for library import and search for other projects. ## utils.bin2c This rule can be used in versions above v2.5.7 to introduce some binary files into the project, and see them as c/c++ header files for developers to use to obtain the data of these files. For example, we can embed some png/jpg resource files into the code in the project. ```lua target("console") set_kind("binary") add_rules("utils.bin2c", {extensions = {".png", ".jpg"}}) add_files("src/*.c") add_files("res/*.png", "res/*.jpg") ``` ::: tip NOTE The setting of extensions is optional, the default extension is .bin ::: Then, we can import and use it through `#include "filename.png.h"`, xmake will automatically generate the corresponding header file for you, and add the corresponding search directory. ```c static unsigned char g_png_data[] = { #include "image.png.h" }; int main(int argc, char** argv) { printf("image.png: %s, size: %d\n", g_png_data, sizeof(g_png_data)); return 0; } ``` The content of the generated header file is similar: ```sh cat build/.gens/test/macosx/x86_64/release/rules/c++/bin2c/image.png.h 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x78, 0x6D, 0x61, 0x6B, 0x65, 0x21, 0x0A, 0x00 ``` ## utils.glsl2spv This rule can be used in v2.6.1 and above. Import glsl shader files such as `*.vert/*.frag` into the project, and then realize automatic compilation to generate `*.spv` files. In addition, we also support binary embedding spv file data in the form of C/C++ header file, which is convenient for program use. ### Compile and generate spv file xmake will automatically call glslangValidator or glslc to compile shaders to generate .spv files, and then output them to the specified `{outputdir = "build"}` directory. ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {outputdir = "build"}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` Note that the `add_packages("glslang")` here is mainly used to import and bind the glslangValidator in the glslang package to ensure that xmake can always use it. Of course, if you have already installed it on your own system, you don’t need to bind this package additionally, but I still recommend adding it. ### Compile and generate c/c++ header files We can also use the bin2c module internally to generate the corresponding binary header file from the compiled spv file, which is convenient for direct import in user code. We only need to enable `{bin2c = true}`. :w ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` Then we can introduce in the code like this: ```c static unsigned char g_test_vert_spv_data[] = { #include "test.vert.spv.h" }; static unsigned char g_test_frag_spv_data[] = { #include "test.frag.spv.h" }; ``` Similar to the usage of bin2c rules, see the complete example: [glsl2spv example](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/glsl2spv) ## utils.hlsl2spv In addition to the `utils.glsl2spv` rule, we now support the `utils.hlsl2spv` rule. ```lua add_rules("mode.debug", "mode.release") add_requires("directxshadercompiler") target("test") set_kind("binary") add_rules("utils.hlsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.hlsl") add_packages("directxshadercompiler") ``` ## python.library We can use this rule to generate python library modules with pybind11, which will adjust the module name of the python library. ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library") add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` with soabi: ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library", {soabi = true}) add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` ## nodejs.module Build nodejs module. ```lua add_requires("node-addon-api") target("foo") set_languages("cxx17") add_rules("nodejs.module") add_packages("node-addon-api") add_files("*.cc") end ``` ## utils.ipsc The ipsc compiler rules are supported and are used as follows. ```lua target("test") set_kind("binary") add_rules("utils.ispc", {header_extension = "_ispc.h"}) set_values("ispc.flags", "--target=host") add_files("src/*.ispc") add_files("src/*.cpp") ``` --- --- url: /api/description/builtin-variables.md --- # Builtin Variables Xmake provides the syntax of `$(varname)` to support the acquisition of built-in variables, for example: ```lua add_cxflags("-I$(builddir)") ``` It will convert the built-in `builddir` 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("$(builddir)/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", "$(builddir)/inc") end) ``` This way of using built-in variables makes the description writing more concise and easy to read. 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)") ``` ::: tip NOTE 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](/api/description/configuration-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](/api/scripts/builtin-modules/os#os-cd), this value will also change. ## var.$(builddir) * 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)") ``` --- --- url: /api/scripts/extension-modules/core/base/bytes.md --- # bytes The bytes module provides binary data buffer operations for handling raw byte data. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.bytes")` ::: ## bytes * Create a byte buffer #### Function Prototype ::: tip API ```lua bytes(size: , initval: ) bytes(str: ) bytes(buffer: , offset: , size: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | size | Required. Buffer size in bytes | | initval | Optional. Initial value for buffer (number or character) | | str | Required. String to convert to bytes object | | buffer | Required. Source bytes buffer for slice | | offset | Required. Offset position in source buffer | | size | Required. Size of the slice | #### Return Value | Type | Description | |------|-------------| | bytes | Returns a bytes buffer object | #### Usage The bytes constructor supports multiple creation methods, providing flexible buffer creation and management. ##### Create a buffer of specified size ```lua -- Create a 1024-byte buffer local buff = bytes(1024) print("Buffer size:", buff:size()) -- Output: 1024 -- Create buffer and initialize with specified value local buff = bytes(100, 0) -- Initialize to 0 local buff = bytes(100, 255) -- Initialize to 255 local buff = bytes(100, 'A') -- Initialize to character 'A' ``` ##### Create from string Create a bytes object from a string, commonly used to convert strings to binary data: ```lua local buff = bytes("hello world") print(buff:size()) -- Output: 11 print(buff:str()) -- Output: hello world ``` ::: warning WARNING bytes objects created from strings are read-only and cannot be modified. ::: ##### Create a slice Create a slice from an existing bytes object, sharing underlying memory without copying data: ```lua local original = bytes("123456789") local slice = bytes(original, 3, 5) -- Slice bytes 3-5 print(slice:str()) -- Output: 345 ``` ##### Concatenate multiple buffers ```lua -- Concatenate using parameter list local buff = bytes(bytes("123"), bytes("456"), bytes("789")) print(buff:str()) -- Output: 123456789 -- Concatenate using array local buff = bytes({bytes("123"), bytes("456"), bytes("789")}) print(buff:str()) -- Output: 123456789 ``` ##### Create empty buffer ```lua local buff = bytes() -- Empty buffer local buff = bytes({}) -- Empty buffer ``` ##### Index operations bytes objects support accessing and modifying individual bytes through indexing (indices start from 1): ```lua local buff = bytes(9) buff:copy("123456789") -- Read byte local byte_value = buff[1] print(byte_value) -- Output: 49 (ASCII code for '1') -- Modify byte buff[1] = string.byte('2') print(buff:str()) -- Output: 223456789 ``` Access slices through range indexing: ```lua local buff = bytes("123456789") local slice = buff[{1, 4}] print(slice:str()) -- Output: 1234 -- Range assignment local buff = bytes(9) buff[{1, 9}] = bytes("123456789") print(buff:str()) -- Output: 123456789 ``` ##### Concatenation operation Use the `..` operator to concatenate two bytes objects, creating a new buffer: ```lua local buff1 = bytes("123") local buff2 = bytes("456") local buff3 = buff1 .. buff2 print(buff3:str()) -- Output: 123456 ``` ## bytes:size * Get buffer size #### Function Prototype ::: tip API ```lua bytes:size() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the number of bytes in the buffer. ```lua local buff = bytes(1024) print(buff:size()) -- Output: 1024 ``` ## bytes:str * Convert to string #### Function Prototype ::: tip API ```lua bytes:str(start: , last: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | start | Start position (optional, default 1) | | last | End position (optional, default buffer size) | #### Usage Converts a bytes object to a string, optionally specifying the conversion range. Parameters: * `start` (optional): Start position, default 1 * `last` (optional): End position, default is buffer size Commonly used to convert data read from network or files to strings: ```lua import("core.base.bytes") local buff = bytes("hello world") print(buff:str()) -- Output: hello world print(buff:str(1, 5)) -- Output: hello print(buff:str(7)) -- Output: world ``` ## bytes:slice * Create a slice #### Function Prototype ::: tip API ```lua bytes:slice(start: , last: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | start | Start position | | last | End position | #### Usage Creates a slice of the buffer, returns a new bytes object that shares the underlying memory (no data copying). Parameters: * `start`: Start position * `last`: End position Slicing is an efficient data access method that avoids data copying: ```lua local buff = bytes("123456789") local slice = buff:slice(3, 5) print(slice:str()) -- Output: 345 print(slice:size()) -- Output: 3 ``` ## bytes:clone * Clone the buffer #### Function Prototype ::: tip API ```lua bytes:clone() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a complete copy of the buffer, allocating new memory and copying all data. Unlike slice, clone copies data, so they don't affect each other: ```lua local original = bytes("hello") local cloned = original:clone() print(cloned:str()) -- Output: hello -- Modifying cloned buffer doesn't affect original cloned[1] = string.byte('H') print(cloned:str()) -- Output: Hello print(original:str()) -- Output: hello (unchanged) ``` ## bytes:copy * Copy data to buffer #### Function Prototype ::: tip API ```lua bytes:copy(src: , start: , last: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | src | Source data, can be string or bytes object | | start | Source data start position (optional, default 1) | | last | Source data end position (optional, default source data size) | #### Usage Copies data from source to the beginning of the buffer. Parameters: * `src`: Source data, can be string or bytes object * `start` (optional): Source data start position, default 1 * `last` (optional): Source data end position, default is source data size ```lua local buff = bytes(9) buff:copy("123456789") print(buff:str()) -- Output: 123456789 -- Copy only partial data local buff = bytes(5) buff:copy("123456789", 5, 9) print(buff:str()) -- Output: 56789 ``` ## bytes:copy2 * Copy data to specified position #### Function Prototype ::: tip API ```lua bytes:copy2(pos: , src: , start: , last: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | pos | Target position | | src | Source data, can be string or bytes object | | start | Source data start position (optional) | | last | Source data end position (optional) | #### Usage Copies data from source to the specified position in the buffer. Parameters: * `pos`: Target position * `src`: Source data, can be string or bytes object * `start` (optional): Source data start position * `last` (optional): Source data end position Used to concatenate multiple segments of data in a buffer: ```lua local buff = bytes(18) buff:copy("123456789") -- Copy to beginning buff:copy2(10, "123456789") -- Copy to position 10 print(buff:str()) -- Output: 123456789123456789 ``` ## bytes:move * Move data to buffer beginning #### Function Prototype ::: tip API ```lua bytes:move(start: , last: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | start | Source data start position | | last | Source data end position (optional) | #### Usage Moves the specified range of data within the buffer to the beginning, supporting memory overlap-safe movement. Parameters: * `start`: Source data start position * `last` (optional): Source data end position ```lua local buff = bytes(9):copy("123456789") buff:move(5, 9) print(buff:str()) -- Output: 567896789 (5-9 moved to beginning) ``` ## bytes:move2 * Move data to specified position #### Function Prototype ::: tip API ```lua bytes:move2(pos: , start: , last: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | pos | Target position | | start | Source data start position | | last | Source data end position (optional) | #### Usage Moves the specified range of data within the buffer to the specified position. Parameters: * `pos`: Target position * `start`: Source data start position * `last` (optional): Source data end position ```lua local buff = bytes(9):copy("123456789") buff:move2(2, 5, 9) print(buff:str()) -- Output: 156789789 ``` ## bytes:u8 * Read unsigned 8-bit integer #### Function Prototype ::: tip API ```lua bytes:u8(offset: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | #### Usage Reads a byte from the specified offset as an unsigned 8-bit integer (0-255). ## bytes:u8\_set * Write unsigned 8-bit integer #### Function Prototype ::: tip API ```lua bytes:u8_set(offset: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | | value | Value to write (0-255) | #### Usage Writes an unsigned 8-bit integer to the specified offset. Writes an unsigned 8-bit integer value to the specified offset. ```lua local buff = bytes(10) buff:u8_set(1, 255) local value = buff:u8(1) print(value) -- Output: 255 ``` ## bytes:s8 * Read signed 8-bit integer #### Function Prototype ::: tip API ```lua bytes:s8(offset: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | #### Usage Reads a byte from the specified offset as a signed 8-bit integer (-128 to 127). ## bytes:u16le * Read unsigned 16-bit integer (little-endian) #### Function Prototype ::: tip API ```lua bytes:u16le(offset: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | #### Usage 参数Reads 2 bytes from the specified offset as an unsigned 16-bit integer (little-endian byte order). ## bytes:u16le\_set * Write unsigned 16-bit integer (little-endian) #### Function Prototype ::: tip API ```lua bytes:u16le_set(offset: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | | value | Value to write (0-65535) | #### Usage Writes an unsigned 16-bit integer to the specified offset (little-endian byte order). ```lua local buff = bytes(10) buff:u16le_set(5, 12346) local value = buff:u16le(5) print(value) -- Output: 12346 ``` ## bytes:u16be * Read unsigned 16-bit integer (big-endian) #### Function Prototype ::: tip API ```lua bytes:u16be(offset: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | #### Usage Reads 2 bytes from the specified offset as an unsigned 16-bit integer (big-endian byte order). ## bytes:u16be\_set * Write unsigned 16-bit integer (big-endian) #### Function Prototype ::: tip API ```lua bytes:u16be_set(offset: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | | value | Value to write (0-65535) | #### Usage Writes an unsigned 16-bit integer to the specified offset (big-endian byte order). ## bytes:u32le * Read unsigned 32-bit integer (little-endian) #### Function Prototype ::: tip API ```lua bytes:u32le(offset: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | #### Usage Reads 4 bytes from the specified offset as an unsigned 32-bit integer (little-endian byte order). ## bytes:u32le\_set * Write unsigned 32-bit integer (little-endian) #### Function Prototype ::: tip API ```lua bytes:u32le_set(offset: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | | value | Value to write (0-4294967295) | #### Usage Writes an unsigned 32-bit integer to the specified offset (little-endian byte order). Used for handling binary protocols and data formats: ```lua local buff = bytes(20) -- Write protocol header (little-endian format) buff:u16le_set(1, 0x1234) -- Magic number buff:u32le_set(3, 100) -- Data length -- Read protocol header local magic = buff:u16le(1) local length = buff:u32le(3) print(string.format("Magic: 0x%04X, Length: %d", magic, length)) ``` ## bytes:u32be * Read unsigned 32-bit integer (big-endian) #### Function Prototype ::: tip API ```lua bytes:u32be(offset: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | #### Usage Reads 4 bytes from the specified offset as an unsigned 32-bit integer (big-endian byte order). ## bytes:u32be\_set * Write unsigned 32-bit integer (big-endian) #### Function Prototype ::: tip API ```lua bytes:u32be_set(offset: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | offset | Byte offset position | | value | Value to write (0-4294967295) | #### Usage Writes an unsigned 32-bit integer to the specified offset (big-endian byte order). ## bytes:dump * Display buffer contents in hexadecimal format #### Function Prototype ::: tip API ```lua bytes:dump(start: , last: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | start | Start position (optional, default 1) | | last | End position (optional, default buffer size) | #### Usage Displays buffer contents in hexadecimal and ASCII format, similar to the hexdump tool. Parameters: * `start` (optional): Start position * `last` (optional): End position Used for debugging binary data, visually displaying memory contents: ```lua local buff = bytes("hello world, this is a test") buff:dump() -- Output similar to: -- 00000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 2C 20 74 68 69 hello world, thi -- 00000010 73 20 69 73 20 61 20 74 65 73 74 s is a test ``` ## bytes:readonly * Check if buffer is read-only #### Function Prototype ::: tip API ```lua bytes:readonly() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns true if the buffer is read-only and cannot be modified. bytes objects created from strings are read-only. ```lua local buff1 = bytes("hello") print(buff1:readonly()) -- Output: true (cannot modify) local buff2 = bytes(10) print(buff2:readonly()) -- Output: false (can modify) -- Attempting to modify read-only buffer will raise error local buff = bytes("test") -- buff[1] = 65 -- Error! Read-only buffer cannot be modified ``` ::: tip TIP bytes objects can be seamlessly used across socket, pipe, lz4, and other modules. Pre-creating large buffers and reusing them can reduce memory allocation overhead. ::: ::: warning WARNING * Indices start from 1 (following Lua convention) * bytes objects created from strings are read-only * Slices share memory with the original bytes object * Pay attention to byte order (big-endian/little-endian) when using integer read/write interfaces ::: --- --- url: /zh/api/scripts/extension-modules/core/base/bytes.md --- # bytes bytes 模块提供了二进制数据缓冲区的操作功能,用于处理原始字节数据。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.bytes")` ::: ## bytes * 创建字节缓冲区 #### 函数原型 ::: tip API ```lua bytes(size: , initval: ) bytes(str: ) bytes(buffer: , offset: , size: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | size | 必需。缓冲区大小(字节数) | | initval | 可选。缓冲区的初始值(数字或字符) | | str | 必需。要转换为 bytes 对象的字符串 | | buffer | 必需。源 bytes 缓冲区(用于切片) | | offset | 必需。在源缓冲区中的偏移位置 | | size | 必需。切片的大小 | #### 返回值说明 | 类型 | 描述 | |------|------| | bytes | 返回一个 bytes 缓冲区对象 | #### 用法说明 bytes 构造函数支持多种创建方式,提供灵活的缓冲区创建和管理。 ##### 创建指定大小的缓冲区 ```lua -- 创建 1024 字节的缓冲区 local buff = bytes(1024) print("缓冲区大小:", buff:size()) -- 输出: 1024 -- 创建缓冲区并初始化为指定值 local buff = bytes(100, 0) -- 初始化为 0 local buff = bytes(100, 255) -- 初始化为 255 local buff = bytes(100, 'A') -- 初始化为字符 'A' ``` ##### 从字符串创建 从字符串创建 bytes 对象,常用于将字符串转换为二进制数据处理: ```lua local buff = bytes("hello world") print(buff:size()) -- 输出: 11 print(buff:str()) -- 输出: hello world ``` ::: warning 注意 从字符串创建的 bytes 对象是只读的,不能修改。 ::: ##### 创建切片 从现有 bytes 对象创建切片,共享底层内存,不复制数据: ```lua local original = bytes("123456789") local slice = bytes(original, 3, 5) -- 切片字节 3-5 print(slice:str()) -- 输出: 345 ``` ##### 连接多个缓冲区 ```lua -- 使用参数列表连接 local buff = bytes(bytes("123"), bytes("456"), bytes("789")) print(buff:str()) -- 输出: 123456789 -- 使用数组连接 local buff = bytes({bytes("123"), bytes("456"), bytes("789")}) print(buff:str()) -- 输出: 123456789 ``` ##### 创建空缓冲区 ```lua local buff = bytes() -- 空缓冲区 local buff = bytes({}) -- 空缓冲区 ``` ##### 索引操作 bytes 对象支持通过索引访问和修改单个字节(下标从 1 开始): ```lua local buff = bytes(9) buff:copy("123456789") -- 读取字节 local byte_value = buff[1] print(byte_value) -- 输出: 49 (字符 '1' 的 ASCII 码) -- 修改字节 buff[1] = string.byte('2') print(buff:str()) -- 输出: 223456789 ``` 通过范围索引访问切片: ```lua local buff = bytes("123456789") local slice = buff[{1, 4}] print(slice:str()) -- 输出: 1234 -- 范围赋值 local buff = bytes(9) buff[{1, 9}] = bytes("123456789") print(buff:str()) -- 输出: 123456789 ``` ##### 连接操作 使用 `..` 操作符连接两个 bytes 对象,创建新的缓冲区: ```lua local buff1 = bytes("123") local buff2 = bytes("456") local buff3 = buff1 .. buff2 print(buff3:str()) -- 输出: 123456 ``` ## bytes:size * 获取缓冲区大小 #### 函数原型 ::: tip API ```lua bytes:size() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回缓冲区的字节数。 ```lua local buff = bytes(1024) print(buff:size()) -- 输出: 1024 ``` ## bytes:str * 转换为字符串 #### 函数原型 ::: tip API ```lua bytes:str(start: , last: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | start | 起始位置(可选,默认 1) | | last | 结束位置(可选,默认为缓冲区大小) | #### 用法说明 将 bytes 对象转换为字符串,可选择指定转换的范围。 参数: * `start`(可选):起始位置,默认 1 * `last`(可选):结束位置,默认为缓冲区大小 常用于从网络或文件读取数据后转换为字符串: ```lua import("core.base.bytes") local buff = bytes("hello world") print(buff:str()) -- 输出: hello world print(buff:str(1, 5)) -- 输出: hello print(buff:str(7)) -- 输出: world ``` ## bytes:slice * 创建切片 #### 函数原型 ::: tip API ```lua bytes:slice(start: , last: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | start | 起始位置 | | last | 结束位置 | #### 用法说明 创建缓冲区的切片,返回新的 bytes 对象,共享底层内存(不复制数据)。 参数: * `start`:起始位置 * `last`:结束位置 切片是一种高效的数据访问方式,避免数据复制: ```lua local buff = bytes("123456789") local slice = buff:slice(3, 5) print(slice:str()) -- 输出: 345 print(slice:size()) -- 输出: 3 ``` ## bytes:clone * 克隆缓冲区 #### 函数原型 ::: tip API ```lua bytes:clone() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建缓冲区的完整副本,分配新的内存并复制所有数据。 与 slice 不同,clone 会复制数据,两者互不影响: ```lua local original = bytes("hello") local cloned = original:clone() print(cloned:str()) -- 输出: hello -- 修改克隆的缓冲区不影响原缓冲区 cloned[1] = string.byte('H') print(cloned:str()) -- 输出: Hello print(original:str()) -- 输出: hello (未改变) ``` ## bytes:copy * 复制数据到缓冲区 #### 函数原型 ::: tip API ```lua bytes:copy(src: , start: , last: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | src | 源数据,可以是字符串或 bytes 对象 | | start | 源数据起始位置(可选,默认 1) | | last | 源数据结束位置(可选,默认为源数据大小) | #### 用法说明 从源数据复制到缓冲区的开头。 参数: * `src`:源数据,可以是字符串或 bytes 对象 * `start`(可选):源数据起始位置,默认 1 * `last`(可选):源数据结束位置,默认为源数据大小 ```lua local buff = bytes(9) buff:copy("123456789") print(buff:str()) -- 输出: 123456789 -- 只复制部分数据 local buff = bytes(5) buff:copy("123456789", 5, 9) print(buff:str()) -- 输出: 56789 ``` ## bytes:copy2 * 复制数据到指定位置 #### 函数原型 ::: tip API ```lua bytes:copy2(pos: , src: , start: , last: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | pos | 目标位置 | | src | 源数据,可以是字符串或 bytes 对象 | | start | 源数据起始位置(可选) | | last | 源数据结束位置(可选) | #### 用法说明 从源数据复制到缓冲区的指定位置。 参数: * `pos`:目标位置 * `src`:源数据,可以是字符串或 bytes 对象 * `start`(可选):源数据起始位置 * `last`(可选):源数据结束位置 用于在缓冲区中拼接多段数据: ```lua local buff = bytes(18) buff:copy("123456789") -- 复制到开头 buff:copy2(10, "123456789") -- 复制到位置 10 print(buff:str()) -- 输出: 123456789123456789 ``` ## bytes:move * 移动数据到缓冲区开头 #### 函数原型 ::: tip API ```lua bytes:move(start: , last: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | start | 源数据起始位置 | | last | 源数据结束位置(可选) | #### 用法说明 将缓冲区内指定范围的数据移动到开头,支持内存重叠安全移动。 参数: * `start`:源数据起始位置 * `last`(可选):源数据结束位置 ```lua local buff = bytes(9):copy("123456789") buff:move(5, 9) print(buff:str()) -- 输出: 567896789 (5-9 移动到开头) ``` ## bytes:move2 * 移动数据到指定位置 #### 函数原型 ::: tip API ```lua bytes:move2(pos: , start: , last: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | pos | 目标位置 | | start | 源数据起始位置 | | last | 源数据结束位置(可选) | #### 用法说明 将缓冲区内指定范围的数据移动到指定位置。 参数: * `pos`:目标位置 * `start`:源数据起始位置 * `last`(可选):源数据结束位置 ```lua local buff = bytes(9):copy("123456789") buff:move2(2, 5, 9) print(buff:str()) -- 输出: 156789789 ``` ## bytes:u8 * 读取无符号 8 位整数 #### 函数原型 ::: tip API ```lua bytes:u8(offset: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | #### 用法说明 从指定偏移位置读取一个字节作为无符号 8 位整数(0-255)。 ## bytes:u8\_set * 写入无符号 8 位整数 #### 函数原型 ::: tip API ```lua bytes:u8_set(offset: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | | value | 要写入的值(0-255) | #### 用法说明 将无符号 8 位整数写入到指定偏移位置。 向指定偏移位置写入一个字节的无符号 8 位整数值。 ```lua local buff = bytes(10) buff:u8_set(1, 255) local value = buff:u8(1) print(value) -- 输出: 255 ``` ## bytes:s8 * 读取有符号 8 位整数 #### 函数原型 ::: tip API ```lua bytes:s8(offset: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | #### 用法说明 从指定偏移位置读取一个字节作为有符号 8 位整数(-128 到 127)。 ## bytes:u16le * 读取无符号 16 位整数(小端) #### 函数原型 ::: tip API ```lua bytes:u16le(offset: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | #### 用法说明 从指定偏移位置读取 2 字节的无符号 16 位整数(小端字节序)。 ## bytes:u16le\_set * 写入无符号 16 位整数(小端) #### 函数原型 ::: tip API ```lua bytes:u16le_set(offset: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | | value | 要写入的值(0-65535) | #### 用法说明 向指定偏移位置写入 2 字节的无符号 16 位整数(小端字节序)。 ```lua local buff = bytes(10) buff:u16le_set(5, 12346) local value = buff:u16le(5) print(value) -- 输出: 12346 ``` ## bytes:u16be * 读取无符号 16 位整数(大端) #### 函数原型 ::: tip API ```lua bytes:u16be(offset: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | #### 用法说明 从指定偏移位置读取 2 字节的无符号 16 位整数(大端字节序)。 ## bytes:u16be\_set * 写入无符号 16 位整数(大端) #### 函数原型 ::: tip API ```lua bytes:u16be_set(offset: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | | value | 要写入的值(0-65535) | #### 用法说明 向指定偏移位置写入 2 字节的无符号 16 位整数(大端字节序)。 ## bytes:u32le * 读取无符号 32 位整数(小端) #### 函数原型 ::: tip API ```lua bytes:u32le(offset: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | #### 用法说明 从指定偏移位置读取 4 字节的无符号 32 位整数(小端字节序)。 ## bytes:u32le\_set * 写入无符号 32 位整数(小端) #### 函数原型 ::: tip API ```lua bytes:u32le_set(offset: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | | value | 要写入的值(0-4294967295) | #### 用法说明 向指定偏移位置写入 4 字节的无符号 32 位整数(小端字节序)。 用于处理二进制协议和数据格式: ```lua local buff = bytes(20) -- 写入协议头(小端格式) buff:u16le_set(1, 0x1234) -- 魔数 buff:u32le_set(3, 100) -- 数据长度 -- 读取协议头 local magic = buff:u16le(1) local length = buff:u32le(3) print(string.format("魔数: 0x%04X, 长度: %d", magic, length)) ``` ## bytes:u32be * 读取无符号 32 位整数(大端) #### 函数原型 ::: tip API ```lua bytes:u32be(offset: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | #### 用法说明 从指定偏移位置读取 4 字节的无符号 32 位整数(大端字节序)。 ## bytes:u32be\_set * 写入无符号 32 位整数(大端) #### 函数原型 ::: tip API ```lua bytes:u32be_set(offset: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | offset | 字节偏移位置 | | value | 要写入的值(0-4294967295) | #### 用法说明 向指定偏移位置写入 4 字节的无符号 32 位整数(大端字节序)。 ## bytes:dump * 以十六进制格式显示缓冲区内容 #### 函数原型 ::: tip API ```lua bytes:dump(start: , last: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | start | 起始位置(可选,默认 1) | | last | 结束位置(可选,默认为缓冲区大小) | #### 用法说明 以十六进制和 ASCII 格式显示缓冲区内容,类似于 hexdump 工具。 参数: * `start`(可选):起始位置 * `last`(可选):结束位置 用于调试二进制数据,直观显示内存内容: ```lua local buff = bytes("hello world, this is a test") buff:dump() -- 输出类似: -- 00000000 68 65 6C 6C 6F 20 77 6F 72 6C 64 2C 20 74 68 69 hello world, thi -- 00000010 73 20 69 73 20 61 20 74 65 73 74 s is a test ``` ## bytes:readonly * 判断缓冲区是否只读 #### 函数原型 ::: tip API ```lua bytes:readonly() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回 true 表示缓冲区只读,不能修改。从字符串创建的 bytes 对象是只读的。 ```lua local buff1 = bytes("hello") print(buff1:readonly()) -- 输出: true (不能修改) local buff2 = bytes(10) print(buff2:readonly()) -- 输出: false (可以修改) -- 尝试修改只读缓冲区会报错 local buff = bytes("test") -- buff[1] = 65 -- 错误! 只读缓冲区不能修改 ``` ::: tip 提示 bytes 对象可以在 socket、pipe、lz4 等模块间无缝传递使用。预先创建大缓冲区并复用可以减少内存分配开销。 ::: ::: warning 注意 * 索引从 1 开始(遵循 Lua 惯例) * 从字符串创建的 bytes 对象是只读的 * 切片和原始 bytes 对象共享内存 * 使用整数读写接口时注意字节序(大端/小端) ::: --- --- url: /examples/cpp/cxx-modules.md --- # C++ Modules Usage & Examples ## 1. Introduction Xmake uses `.mpp` as the default module extension, but also supports `.ixx`, `.cppm`, `.mxx`, etc. It fully supports C++20 Modules with gcc11/clang/msvc, and can automatically analyze module dependencies for maximum parallel compilation. **Basic usage:** ```lua set_languages("c++20") target("class") set_kind("binary") add_files("src/*.cpp", "src/*.mpp") ``` > More official examples: [C++ Modules Examples](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c%2B%2B/modules) *** ## 2. Advanced Usage ### 2.1 Cpp-only Project with Modules From v2.7.1, Headerunits are supported. Normally, at least one `.mpp` file is needed to enable modules, but you can also force it: ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") set_languages("c++20") set_policy("build.c++.modules", true) ``` ### 2.2 Headerunits Example See [headerunits example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c%2B%2B/modules/headerunits) for how to use STL or custom headers as headerunits. *** ## 3. Module Package Distribution & Integration ### 3.1 Distributing C++ Modules as Packages Specify `{install = true}` for module files to be distributed: ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") target("foo") set_kind("static") add_files("*.cpp") add_files("*.mpp", { install = true }) ``` You can make it a package for [xmake-repo](https://github.com/xmake-io/xmake-repo) or local/private repo. Local package example: ```lua package("foo") set_sourcedir(path.join(os.scriptdir(), "src")) on_install(function(package) import("package.tools.xmake").install(package, {}) end) ``` ### 3.2 Integrating C++ Modules Packages Quickly integrate with `add_requires("foo")`: ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") add_repositories("my-repo my-repo") add_requires("foo", "bar") target("packages") set_kind("binary") add_files("src/*.cpp") add_packages("foo", "bar") set_policy("build.c++.modules", true) ``` *** ## 4. C++23 Standard Library Modules Support for C++23 stdmodules: ```lua add_rules("mode.debug", "mode.release") set_languages("c++latest") target("mod") set_kind("static") add_files("src/*.cpp") add_files("src/*.mpp", {public = true}) target("stdmodules") set_kind("binary") add_files("test/*.cpp") add_deps("mod") ``` ```c++ // my_module.mpp export module my_module; import std; export auto my_sum(std::size_t a, std::size_t b) -> std::size_t; ``` *** ## 5. More C++ Modules Example Collection The official [C++ Modules Example Collection](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c%2B%2B/modules) provides a variety of C++20/23 Modules projects, each subdirectory is a standalone example: * **basic**: Basic module usage * **class**: Exporting classes in modules * **headerunits**: Using headerunits * **import\_std**: Standard library modules * **partition**: Module partitions * **packages**: Module package distribution & integration * **stdmodules**: C++23 standard library modules Each example contains a complete `xmake.lua` and source code for in-depth learning and reference. --- --- url: /zh/examples/cpp/cxx-modules.md --- # C++ Modules 使用与示例 {#cxx-modules} ## 基础介绍 {#introduction} Xmake 采用 `.mpp` 作为默认的模块扩展名,同时也支持 `.ixx`、`.cppm`、`.mxx` 等。已完整支持 gcc11/clang/msvc 的 C++20 Modules 构建,并能自动分析模块间依赖,实现最大化并行编译。 **最基础用法:** ```lua set_languages("c++20") target("class") set_kind("binary") add_files("src/*.cpp", "src/*.mpp") ``` > 更多官方示例见:[C++ Modules 示例集](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c%2B%2B/modules) *** ## 进阶用法 {#advanced-usage} ### 仅 Cpp 工程启用 Modules v2.7.1 起支持 Headerunits,可在模块中引入 STL 和用户头文件模块。通常需至少有一个 `.mpp` 文件才会启用 modules 编译,但也可通过: ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") set_languages("c++20") set_policy("build.c++.modules", true) ``` ### Headerunits 示例 * 如何将 STL 或自定义头文件作为 headerunit 引入模块,见 [headerunits 示例](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c%2B%2B/modules/headerunits)。 *** ## 模块包分发与集成 {#distribution} ### 分发 C++ Modules 包 通过 `{install = true}` 指定需要安装分发的模块文件: ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") target("foo") set_kind("static") add_files("*.cpp") add_files("*.mpp", { install = true }) ``` 可将其做成包,提交到 [xmake-repo](https://github.com/xmake-io/xmake-repo) 或本地/私有仓库。 本地包示例: ```lua package("foo") set_sourcedir(path.join(os.scriptdir(), "src")) on_install(function(package) import("package.tools.xmake").install(package, {}) end) ``` ### 集成 C++ Modules 包 通过 `add_requires("foo")` 快速集成: ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") add_repositories("my-repo my-repo") add_requires("foo", "bar") target("packages") set_kind("binary") add_files("src/*.cpp") add_packages("foo", "bar") set_policy("build.c++.modules", true) ``` *** ## C++23 标准库模块 {#stdmodules} 支持 C++23 标准库模块(stdmodules): ```lua add_rules("mode.debug", "mode.release") set_languages("c++latest") target("mod") set_kind("static") add_files("src/*.cpp") add_files("src/*.mpp", {public = true}) target("stdmodules") set_kind("binary") add_files("test/*.cpp") add_deps("mod") ``` ```c++ // my_module.mpp export module my_module; import std; export auto my_sum(std::size_t a, std::size_t b) -> std::size_t; ``` *** ## 更多 C++ Modules 示例集锦 {#examples} Xmake 官方仓库 [C++ Modules 示例集](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c%2B%2B/modules) 提供了丰富的 C++20/23 Modules 工程,每个子目录为一个独立示例: * **basic**:基础模块用法 * **class**:模块中导出类 * **headerunits**:Headerunits 用法 * **import\_std**:标准库模块 * **partition**:模块分区 * **packages**:模块包分发与集成 * **stdmodules**:C++23 标准库模块 每个示例目录下均有完整的 `xmake.lua` 和源码,适合深入学习和参考。 --- --- url: /api/scripts/extension-modules/cli/amalgamate.md --- # cli.amalgamate * Merge into single source file This is a small utility module for quickly merging all c/c++ and header sources inside a given target into a single source file. The merge expands all the internal includes headers and generates a DAG, which is introduced by topological sorting. By default it will handle the merge of all targets, for example: ```sh $ xmake l cli.amalgamate build/tbox.c generated! build/tbox.h generated! ``` We can also specify the targets we need for the merge: ```sh $ xmake l cli.amalgamate tbox build/tbox.c generated! build/tbox.h generated! ``` You can also handle symbol conflicts by specifying a custom unique ID macro definition for each source file you merge. ```sh $ xmake l cli.amalgamate -u MY_UNIQUEU_ID build/tbox.c generated! build/tbox.h generated! ``` If there are renamed symbols in multiple source files, you can determine if the `MY_UNIQUEU_ID` macro is defined, and if it is, then it is in a single file, so you can handle the renamed symbols yourself in the source code. ```c #ifdef MY_UNIQUEU_ID // do some thing #endif ``` We can also specify the output location: ```sh $ xmake l cli.amalgamate -o /xxx /xxx/tbox.c generated! /xxx/tbox.h generated! ``` --- --- url: /zh/api/scripts/extension-modules/cli/amalgamate.md --- # cli.amalgamate * 合并成单源码文件 这是一个小工具模块,主要用于快速合并指定 target 里面的所有 c/c++ 和 头文件源码到单个源文件。 合并会将内部 includes 头文件全部展开,并生成 DAG,通过拓扑排序引入。 默认它会处理所有 target 的合并,例如: ```sh $ xmake l cli.amalgamate build/tbox.c generated! build/tbox.h generated! ``` 我们也可以指定合并需要的目标: ```sh $ xmake l cli.amalgamate tbox build/tbox.c generated! build/tbox.h generated! ``` 也可以在合并每个源文件时候,指定一个自定义的 unique ID 的宏定义,来处理符号冲突问题。 ```sh $ xmake l cli.amalgamate -u MY_UNIQUEU_ID build/tbox.c generated! build/tbox.h generated! ``` 如果多个源文件内部有重名符号,就可以判断这个 `MY_UNIQUEU_ID` 宏是否被定义,如果定义了,说明是在单文件中,就自己在源码中处理下重名符号。 ```c #ifdef MY_UNIQUEU_ID // do some thing #endif ``` 我们也可以指定输出位置: ```sh $ xmake l cli.amalgamate -o /xxx /xxx/tbox.c generated! /xxx/tbox.h generated! ``` --- --- url: /api/description/conditions.md --- # Conditions Conditions are generally used to handle some special compilation platforms. ## is\_os ### Is the current compilation target system #### Function Prototype ::: tip API ```lua is_os(os: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | os | Operating system name string | | ... | Variable parameters, can pass multiple OS names | #### Usage ```lua if is_os("ios") then add_files("src/xxx/*.m") end ``` Support operation systems: * windows * linux * android * macosx * ios ## is\_arch ### Is the current compilation architecture #### Function Prototype ::: tip API ```lua is_arch(arch: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | arch | Architecture name string | | ... | Variable parameters, can pass multiple architecture names | #### Usage You can use this api to check the configuration command: `xmake f -a armv7` ```lua -- if the current architecture is x86_64 or i386 if is_arch("x86_64", "i386") then add_files("src/xxx/*.c") end -- if the current architecture is armv7 or arm64 or armv7s or armv7-a if is_arch("armv7", "arm64", "armv7s", "armv7-a") then -- ... end ``` And you can also use the lua regular expression: `.*` to check all matched architectures. ```lua -- if the current architecture is arm which contains armv7, arm64, armv7s and armv7-a ... if is_arch("arm.*") then -- ... end ``` ## is\_plat ### Is the current compilation platform #### Function Prototype ::: tip API ```lua is_plat(plat: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | plat | Platform name string | | ... | Variable parameters, can pass multiple platform names | #### Usage You can use this api to check the configuration command: `xmake f -p iphoneos` ```lua -- if the current platform is android if is_plat("android") then add_files("src/xxx/*.c") end -- if the current platform is macosx or iphoneos if is_plat("macosx", "iphoneos") then add_frameworks("Foundation") end ``` Available platforms: | Platform | |-----------| | android | | appletvos | | applexros | | bsd | | cross | | cygwin | | haiku | | iphoneos | | linux | | macosx | | mingw | | msys | | wasm | | watchos | | windows | ## is\_host ### Is the current compilation host system #### Function Prototype ::: tip API ```lua is_host(host: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | host | Host system name string | | ... | Variable parameters, can pass multiple host names | #### Usage Some compilation platforms can be built on multiple different operating systems, for example: android ndk (on linux, macOS and windows). So, we can use this api to determine the current host operating system. ```lua if is_host("windows") then add_includedirs("C:\\includes") else add_includedirs("/usr/includess") end ``` Support hosts: * windows * linux * macosx We can also get it from [$(host)](/api/description/builtin-variables#var-host) or [os.host](/api/scripts/builtin-modules/os#os-host). ## is\_subhost ### Determine the subsystem environment of the current host #### Function Prototype ::: tip API ```lua is_subhost(subhost: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | subhost | Subsystem name string | | ... | Variable parameters, can pass multiple subsystem names | #### Usage At present, it is mainly used for detection of cygwin, msys2 and other subsystem environments on windows systems. If you run xmake in the msys2 shell environment, then `is_subhost("windows")` will return false, and `is_host("windows")` It will still return true. Currently supported subsystems: * msys * cygwin Configuration example: ```lua if is_subhost("msys", "cygwin") then - Currently in the shell environment of msys2/cygwin end ``` We can also quickly check the current subsystem platform by executing `xmake l os.subhost`. ::: tip NOTE It may also support other subsystem environments under linux and macos systems later, if they exist. ::: ## is\_subarch ### Determine the architecture of the current host subsystem environment #### Function Prototype ::: tip API ```lua is_subarch(subarch: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | subarch | Subsystem architecture name string | | ... | Variable parameters, can pass multiple subsystem architecture names | #### Usage At present, it is mainly used for the detection of the architecture under the subsystem environment such as cygwin and msys2 on the windows system. The msvc tool chain is usually used on the windows compilation platform, and the architecture is x64, x86. In the msys/cygwin subsystem environment, the compiler architecture defaults to x86\_64/i386, which is different. We can also quickly view the current subsystem architecture by executing `xmake l os.subarch`. ## is\_cross ### Determines whether the current platform is cross-compiled or not. #### Function Prototype ::: tip API ```lua is_cross() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | - | No parameters | #### Usage This interface returns true if the current target architecture and platform, which is not the current host platform, is cross-compiled. ## is\_mode ### Is the current compilation mode #### Function Prototype ::: tip API ```lua is_mode(mode: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | mode | Compilation mode name string | | ... | Variable parameters, can pass multiple mode names | #### Usage You can use this api to check the configuration command: `xmake f -m debug` The compilation mode is not builtin mode for xmake, so you can set the mode value by yourself. We often use these configuration values: `debug`, `release`, `profile`, etc. ```lua -- if the current compilation mode is debug? if is_mode("debug") then add_defines("DEBUG") set_symbols("debug") set_optimize("none") end -- if the current compilation mode is release? if is_mode("release") then set_symbols("hidden") set_strip("all") end ``` ## is\_kind ### Is the current target kind #### Function Prototype ::: tip API ```lua is_kind(kind: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | kind | Target kind name string | | ... | Variable parameters, can pass multiple kind names | #### Usage You can use this api to check the configuration command: `xmake f -k [static|shared]` ```lua target("test") -- set target kind from the configuration command set_kind("$(kind)") add_files("src/*c") -- compile target for static? if is_kind("static") then add_files("src/xxx.c") end ``` You can switch the target kind by configuration command. ```sh # compile as static library $ xmake f -k static $ xmake ``` ```sh # compile as shared library $ xmake f -k shared $ xmake ``` ## is\_config ### Is the given config values? #### Function Prototype ::: tip API ```lua is_config(name: , values: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration option name string | | values | Configuration value string | | ... | Variable parameters, can pass multiple values | #### Usage This interface is introduced from version 2.2.2 to determine whether the specified configuration is a given value. For example: ```sh $ 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 ``` Can be used for conditional package requirements, eg. : ```lua -- Add lua or luajit package requirements, depending on the lua_flavor option value option("lua_flavor") set_showmenu(true) set_values("luajit", "lua") option_end() if is_config("lua_flavor", "luajit") then add_requires("luajit") elseif is_config("lua_flavor", "lua") then add_requires("lua") 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 ``` ::: tip NOTE This interface is not only able to determine the custom options defined through the [option](/api/description/configuration-option#option), but also to determine the built-in global and local configuration. ::: ## has\_config ### Is the given configs enabled? #### Function Prototype ::: tip API ```lua has_config(configs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | configs | Configuration name string | | ... | Variable parameters, can pass multiple configuration names | #### Usage 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: ```sh # 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: ```sh # disable config/option(if be boolean type) $ xmake f --test1=n $ xmake f --test1=no $ xmake f --test1=false ``` ::: tip NOTE This interface can determine not only the built-in global and local configs, but also the custom options defined through the [option](/api/description/configuration-option#option). This interface works together with the [get\_config](/api/description/global-interfaces#get-config) interface to completely get and determine the option state set by the user through `xmake f --option1=xxx`. ::: ## has\_package ### Is the given dependent package enabled? #### Function Prototype ::: tip API ```lua has_package(packages: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | packages | Package name string | | ... | Variable parameters, can pass multiple package names | #### Usage 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](/api/description/global-interfaces#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. ::: tip NOTE The difference between this interface and [has\_config](#has_config) is that [has\_config](#has_config) is used for [option](/api/description/configuration-option#option) whereas this is used for [add\_requires](/api/description/global-interfaces#add-requires). ::: --- --- url: /api/description/configuration-option.md --- # 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. :::tip NOTE 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") -- ... ``` :::tip NOTE 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. ::: ## option ### Defining options #### Function Prototype ::: tip API ```lua option(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Option name string | #### Usage 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) 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 #### Function Prototype ::: tip API ```lua option_end() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | - | No parameters | #### Usage This is an optional api that shows the departure option scope, similar to [target\_end](/api/description/project-target#target-end). ## add\_deps ### Adding options depends #### Function Prototype ::: tip API ```lua add_deps(deps: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | deps | Dependency option name string or array | | ... | Variable parameters, can pass multiple dependency names | #### Usage 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](#on_check). ```lua option("small") set_default(true) on_check(function (option) -- ... end) option("test") add_deps("small") set_default(true) after_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. :::tip NOTE Since on\_check will only be executed when the default value is not set, if the default value is set, the custom logic can be processed in the after\_check phase. ::: ## before\_check Execute this script before option detection #### Function Prototype ::: tip API ```lua before_check(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before check script function with option parameter | #### Usage ```lua option("zlib") before_check(function (option) end) ``` ## on\_check ### Custom Option Detection Script #### Function Prototype ::: tip API ```lua on_check(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Check script function with option parameter | #### Usage This script overrides the built-in option detection logic. ```lua option("test") add_deps("small") 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. :::tip NOTE Only when `set_default` is not set, will the `on_check` be executed for custom option check script. ::: ## after\_check Execute this script after option detection #### Function Prototype ::: tip API ```lua after_check(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After check script function with option parameter | #### Usage 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) ``` ## set\_values ### Setting the list of option values #### Function Prototype ::: tip API ```lua set_values(values: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | values | Option value string or array | | ... | Variable parameters, can pass multiple values | #### Usage 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_values("a", "b", "c") ``` The effect chart is as follows: ## set\_default ### Setting options defaults #### Function Prototype ::: tip API ```lua set_default(value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | value | Default value (string, boolean, or number) | #### Usage 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 [has\_config](/api/description/conditions#has-config), and the option is enabled. You can use the [get\_config](/api/description/global-interfaces#get-config) and [has\_config](/api/description/conditions#has-config) interfaces to get the defined option state, which is the state value set by the user through `xmake f --option1=xxx`. 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)") 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: ```sh $ xmake f --rootdir=~/projectdir/src $ xmake ``` Specify a different source directory path for this `rootdir` option and compile it. Detection behavior of the | 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 | ## set\_showmenu ### Set whether to enable menu display #### Function Prototype ::: tip API ```lua set_showmenu(showmenu: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | showmenu | Whether to show in menu (boolean) | #### Usage 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 ``` :::tip NOTE After 2.6.8, this option is enabled by default and there is usually no need to configure it additionally. ::: ## set\_category ### Setting option categories, only for menu display #### Function Prototype ::: tip API ```lua set_category(category: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | category | Category name string | #### Usage 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_category("test") option("test2") set_category("test") option("demo1") set_category("demo") option("demo2") set_category("demo") ``` The four options here are grouped into two groups: `test` and `demo`, and the layout shown is similar to this: ```sh 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_category("root menu/test1") -- 'choice' option with values: "a", "b", "c" option("test2") set_default("a") set_values("a", "b", "c") set_category("root menu/test2") -- 'string' option option("test3") set_default("xx") set_category("root menu/test3/test3") -- 'number' option option("test4") set_default(6) 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: ## set\_description ### Setting menu display description #### Function Prototype ::: tip API ```lua set_description(description: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | description | Description string or array | | ... | Variable parameters, can pass multiple description lines | #### Usage 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_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_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: ```sh $ xmake f --mode=release ``` ## add\_links ### Add Link Library Detection #### Function Prototype ::: tip API ```lua add_links(links: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | links | Library name string or array | | ... | Variable parameters, can pass multiple library names | #### Usage 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") 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 ## add\_linkdirs ### Adding the search directory needed for link library detection #### Function Prototype ::: tip API ```lua add_linkdirs(linkdirs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | linkdirs | Link directory path string or array | | ... | Variable parameters, can pass multiple directory paths | #### Usage 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](#add_links) ## add\_rpathdirs ### Adding a load search directory for a dynamic library at runtime #### Function Prototype ::: tip API ```lua add_rpathdirs(rpathdirs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | rpathdirs | RPATH directory path string or array | | ... | Variable parameters, can pass multiple directory paths | #### Usage After the option passes the detection, it will be automatically added to the corresponding target. For details, see: [target.add\_rpathdirs](/api/description/project-target#add-rpathdirs). ## add\_cincludes ### Add c header file detection #### Function Prototype ::: tip API ```lua add_cincludes(includes: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Header file name string or array | | ... | Variable parameters, can pass multiple header file names | #### Usage This option will be enabled if the c header file is passed, for example: ```lua option("pthread") 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](#on_check) via [lib.detect.has\_cincludes](#detect-has_cincludes). ## add\_cxxincludes ### Add c++ header file detection #### Function Prototype ::: tip API ```lua add_cxxincludes(includes: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | C++ header file name string or array | | ... | Variable parameters, can pass multiple header file names | #### Usage Similar to [add\_cincludes](#add_cincludes), except that the detected header file type is a c++ header file. ## add\_ctypes ### Add c type detection #### Function Prototype ::: tip API ```lua add_ctypes(types: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | C type name string or array | | ... | Variable parameters, can pass multiple type names | #### Usage This option will be enabled if the c type is passed, for example: ```lua option("wchar") add_ctypes("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](#on_check) via [lib.detect.has\_ctypes](#detect-has_ctypes). ## add\_cxxtypes ### Adding c++ type detection #### Function Prototype ::: tip API ```lua add_cxxtypes(types: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | C++ type name string or array | | ... | Variable parameters, can pass multiple type names | #### Usage Similar to [add\_ctypes](#add_ctypes), except that the type detected is a c++ type. ## add\_csnippets ### Add c code fragment detection #### Function Prototype ::: tip API ```lua add_csnippets(name: , code: , { tryrun = , output = , number = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Code snippet name string | | code | C code fragment string | | tryrun | Whether to try running the code | | output | Whether to capture output | | number | Whether to parse output as number | #### Usage If the existing [add\_ctypes](#add_ctypes), [add\_cfuncs](#add_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\_cxxsnippets](#add_cxxsnippets). ## add\_cxxsnippets ### Adding c++ code snippet detection #### Function Prototype ::: tip API ```lua add_cxxsnippets(name: , code: , { tryrun = , output = , number = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Code snippet name string | | code | C++ code fragment string | | tryrun | Whether to try running the code | | output | Whether to capture output | | number | Whether to parse output as number | #### Usage 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_cxxsnippets("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](#on_check) via [lib.detect.check\_cxsnippets](#detect-check_cxsnippets). After v2.5.7, two new options, `{tryrun = true}` and `{output = true}`, are added to try to run detection and capture output. Setting tryrun can try to run to detect: ```lua option("test") add_cxxsnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0: -1;", {tryrun = true}) ``` Setting output will also try to detect and additionally capture the output content of the run. ```lua option("test") add_cxxsnippets("INT_SIZE",'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` :::tip NOTE Set to capture output, the current option cannot set other snippets ::: We can also get the output bound to the option through `is_config`. ```lua if is_config("test", "8") then - xxx end ``` ## add\_cfuncs ### Add c library function detection #### Function Prototype ::: tip API ```lua add_cfuncs(funcs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | funcs | C function name string or array | | ... | Variable parameters, can pass multiple function names | #### Usage ```lua option("setjmp") add_cincludes("setjmp.h") add_cfuncs("sigsetjmp", "setjmp") add_defines("HAVE_SETJMP") target("test") add_options("setjmp") ``` This option detects whether there are some interfaces of `setjmp`. If the test passes, the target program of `test` will add the macro definition of `HAVE_SETJMP`. The function fragments inside support the following syntax formats: ```lua -- Simply detect whether the function address exists, and internally will try to determine its address sigsetjmp -- If some functions are defined by macro wrap, the detection can be bypassed in this way sigsetjmp((void*)0, 0) -- You can also specify a complete function statement, for example: funcname{codebody} sigsetjmp{sigsetjmp((void*)0, 0);} sigsetjmp{int a = 0; sigsetjmp((void*)a, a);} ``` :::tip NOTE Note that the detected function usually needs to be accompanied by `add_cincludes` to ensure that the function can be included normally, otherwise the detection will fail. ::: ## add\_cxxfuncs ### Add c++ library function detection #### Function Prototype ::: tip API ```lua add_cxxfuncs(funcs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | funcs | C++ function name string or array | | ... | Variable parameters, can pass multiple function names | #### Usage The usage is consistent with [add\_cfuncs](#add_cxxfuncs). --- --- url: /guide/project-configuration/configure-targets.md --- # Configure Targets {#configure-targets} After creating an empty project, we will get the following basic configuration file. ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") ``` Among them, `mode.release` is the default compilation mode rule, which will automatically add some common optimization compilation options for the target build, such as: `-O2`, and so on. We can also switch to `mode.debug` debugging mode through `xmake f -m debug`, which will automatically configure some debugging options, such as: `-g`, and so on. Of course, we can also configure them completely by ourselves without using the mode rules. ```lua [xmake.lua] target("test") set_kind("binary") add_files("src/*.cpp") ``` ## Configure macro definition {#configure-defines} We can add a macro definition option to the target program through [add\_defines](/api/description/project-target#add-defines), for example: ```lua [xmake.lua] target("test") set_kind("binary") add_files("src/*.cpp") add_defines("DEBUG") ``` In the test target scope, we configure a macro definition compilation option `-DDEBUG` for it, which will only take effect on the test target. We can also use the `xmake -v` command to quickly verify whether the added configuration is effective. ```sh [ 23%]: cache compiling.release src/main.cpp /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Q unused-arguments -target x86_64-apple-macos15.2 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -fvisibility=hidden -fvisibility-inline s-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp ``` As you can see, `-DDEBUG` has been added to the clang compilation command. ## Configure multiple targets at the same time {#configure-multi-targets} If you want multiple compilation targets to take effect at the same time, we can move them to the global root scope. ```lua [xmake.lua] add_defines("DEBUG") target("foo") set_kind("binary") add_files("src/*.cpp") target("bar") set_kind("binary") add_files("src/*.cpp") ``` At this time, both foo and bar target programs will have the `-DDEBUG` compilation option. The configuration of the root scope will affect the current xmake.lua configuration file and the target programs in all sub-xmake.lua files (that is, the sub-directory configuration introduced by includes). ```lua [xmake.lua] add_defines("DEBUG") target("foo") set_kind("binary") add_files("src/*.cpp") includes("bar") ``` ```lua [bar/xmake.lua] target("bar") set_kind("binary") add_files("src/*.cpp") ``` If the bar target is in the `bar/xmake.lua` sub-configuration file, it will also be effective. ::: tip NOTE However, the root scope configuration in the sub-file cannot affect the target in the same level or parent configuration file. ::: ```lua [xmake.lua] target("foo") set_kind("binary") add_files("src/*.cpp") includes("bar") ``` ```lua [bar/xmake.lua] add_defines("DEBUG") target("bar") set_kind("binary") add_files("src/*.cpp") target("zoo") set_kind("binary") add_files("src/*.cpp") ``` Here, although `add_defines` is configured in the root scope, it only affects the bar and zoo targets, and cannot affect the foo target. In other words, the scope of influence of the root scope is in a tree structure, and it takes effect layer by layer. ## Configure optimization options {#configure-optimization} `mode.release` will automatically introduce some optimization options, and we can also configure them ourselves through the [set\_optimize](/api/description/project-target#set-optimize) interface. For example: ```lua set_optimize("faster") ``` It will configure the `-O2` compilation option for the target. ::: tip NOTE Different compilers have different optimization options, and xmake will automatically map to the most appropriate compilation option. ::: For more details, please refer to the document: [set\_optimize](/api/description/project-target#set-optimize). ## Configure header file search directory {#configure-includedirs} ```lua add_includedirs("/tmp") ``` It will add the `-I/tmp` compilation option to the compiler's command line. For more details, please refer to the document: [add\_includedirs](/api/description/project-target#add-includedirs). ## Configure link library search directory {#configure-linkdirs} ```lua add_linkdirs("/tmp") ``` It will add the `-L/tmp` link option to the linker command line. For more details, please refer to the document: [add\_linkdirs](/api/description/project-target#add-linkdirs). ## Configure link library {#configure-links} ```lua add_links("foo") ``` It will add the `-lfoo` link option to the linker command line, which usually needs to be used with [add\_linkdirs](/api/description/project-target#add-linkdirs). ```lua add_links("foo") add_linkdirs("/tmp") ``` For more details, please refer to the document: [add\_links](/api/description/project-target#add-links). ## Configure system link library {#configure-syslinks} `add_links` is usually used to link user-generated libraries, while `add_syslinks` can add system libraries without the need for an additional `add_linkdirs`. And its link order is relatively late (after user libraries). ```lua add_links("foo") add_syslinks("pthread", "dl") ``` If links and syslinks are configured at the same time, the link order is as follows: ```sh -lfoo -lpthread -ldl ``` ## Configure original compilation options {#configure-flags} The `add_defines` and other interfaces mentioned above are all abstract APIs. Since most compilers support them, Xmake abstracts them to make them more convenient for users to use and provide better cross-platform and cross-compiler support. However, we can also add specific compilation options for C++ code through the `add_cxxflags` interface, such as: ```lua add_cxflags("-DDEBUG") ``` It is equivalent to `add_defines("DEBUG")`, but `add_defines` is more general and applicable to all compilers, while `add_cxxflags("-DDEBUG")` may only be applicable to a few compilers, because not all compilers define macros through `-D`. In addition, we can also add compilation options for C code through the `add_cflags` interface, and `add_cxflags` to add compilation options for C/C++ code at the same time. For more details, please refer to the documentation: * [add\_cflags](/api/description/project-target#add-cflags) * [add\_cxflags](/api/description/project-target#add-cxflags) * [add\_cxxflags](/api/description/project-target#add-cxxflags) ## Target Type Configuration {#configure-target-types} ### Setting Target Types You can set different target types through `set_kind()`: ```lua target("app") set_kind("binary") -- executable program add_files("src/*.cpp") target("lib") set_kind("static") -- static library add_files("src/*.cpp") target("dll") set_kind("shared") -- dynamic library add_files("src/*.cpp") target("obj") set_kind("object") -- object file collection add_files("src/*.cpp") target("header") set_kind("headeronly") -- header-only library add_headerfiles("include/*.h") ``` ### Target Type Description | Type | Description | Output Files | |------|-------------|--------------| | binary | Executable program | app.exe, app | | static | Static library | libapp.a, app.lib | | shared | Dynamic library | libapp.so, app.dll | | object | Object file collection | \*.o, \*.obj | | headeronly | Header-only library | No compilation output | | phony | Virtual target | No output, only for dependency management | ### Virtual Targets (phony) Virtual targets don't generate actual files, they're only used for managing dependencies: ```lua target("test1") set_kind("binary") add_files("src/test1.cpp") target("test2") set_kind("binary") add_files("src/test2.cpp") target("all-tests") set_kind("phony") add_deps("test1", "test2") ``` Executing `xmake build all-tests` will build both test1 and test2. ## Configure target dependency {#configure-targetdeps} We can configure the dependency between two target programs through the [add\_deps](/api/description/project-target#add-deps) interface. This is usually used in scenarios where an executable program depends on a static library (or dynamic library), for example: ```lua target("foo") set_kind("static") add_files("src/foo.cpp") target("test") set_kind("binary") add_deps("foo") add_files("src/main.cpp") ``` Since the dependency between test and foo library programs is configured through `add_deps`, when we compile test, the foo dependency library will be automatically compiled and automatically linked to it, without the need for additional `add_links` and `add_linkdirs` configurations. For more information about dependency configuration, please refer to the document: [add\_deps](/api/description/project-target#add-deps). ## Target File Configuration {#configure-target-files} ### Adding Source Files ```lua target("app") add_files("src/*.cpp") -- add all cpp files add_files("src/*.c") -- add all c files add_files("src/*.asm") -- add assembly files add_files("src/*.m") -- add Objective-C files add_files("src/*.mm") -- add Objective-C++ files ``` ### Excluding Specific Files ```lua target("app") add_files("src/*.cpp|test.cpp") -- exclude src/test.cpp file ``` ### Adding Header Files ```lua target("header-lib") set_kind("headeronly") add_headerfiles("include/*.h") -- add header files add_headerfiles("include/*.hpp") -- add C++ header files add_headerfiles("include/*.h", {install = false}) -- non-installable header files ``` ### Adding Install Files ```lua target("app") add_installfiles("assets/*.png", {prefixdir = "share/app"}) -- install resource files add_installfiles("config/*.conf", {prefixdir = "etc"}) -- install config files ``` ## Target Property Configuration {#configure-target-properties} ### Setting Target Filename ```lua target("app") set_basename("myapp") -- generated filename will be myapp.exe ``` ### Setting Target Directory ```lua target("app") set_targetdir("bin") -- output to bin directory ``` ### Setting Install Directory ```lua target("app") set_installdir("bin") -- install to bin directory ``` ### Setting Version Information ```lua target("app") set_version("1.0.0") -- set version number ``` ## Target Visibility Configuration {#configure-target-visibility} ### Setting Symbol Visibility ```lua target("lib") set_kind("shared") set_symbols("hidden") -- hide symbols, reduce export table size ``` ## Target Optimization Configuration {#configure-target-optimization} ### Setting Optimization Level ```lua target("app") set_optimize("fast") -- fast optimization set_optimize("faster") -- faster optimization set_optimize("fastest") -- fastest optimization set_optimize("smallest") -- size optimization set_optimize("none") -- no optimization ``` ### Setting Debug Information ```lua target("app") set_symbols("debug") -- add debug symbols set_strip("debug") -- strip debug symbols during linking set_strip("all") -- strip all symbols during linking ``` ## Target Language Configuration {#configure-target-languages} ### Setting Language Standards ```lua target("app") set_languages("c++17") -- set C++ standard set_languages("c11") -- set C standard ``` ### Setting Language Features ```lua target("app") set_languages("c++17", "c11") -- support both C++17 and C11 ``` ## Target Platform Configuration {#configure-target-platforms} ### Setting Target Platform ```lua target("app") set_plat("android") -- set to Android platform set_arch("arm64-v8a") -- set to ARM64 architecture ``` ### Conditional Configuration ```lua target("app") if is_plat("windows") then add_defines("WIN32") add_links("user32") elseif is_plat("linux") then add_defines("LINUX") add_links("pthread") end ``` ## Target Option Configuration {#configure-target-options} ### Associating Options ```lua option("enable_gui") set_default(false) set_description("Enable GUI support") target("app") set_options("enable_gui") -- associate option ``` ### Conditional Options ```lua target("app") if has_config("enable_gui") then add_defines("GUI_ENABLED") add_links("gtk+-3.0") end ``` ## Target Rule Configuration {#configure-target-rules} ### Adding Build Rules ```lua target("app") add_rules("mode.debug", "mode.release") -- add debug and release modes add_rules("qt.widgetapp") -- add Qt application rule add_rules("wdk.driver") -- add WDK driver rule ``` ### Custom Rules ```lua rule("myrule") set_extensions(".my") on_build_file(function (target, sourcefile, opt) -- custom build logic end) target("app") add_rules("myrule") -- apply custom rule ``` ## Target Runtime Configuration {#configure-target-runtime} ### Setting Runtime Library ```lua target("app") set_runtimes("MT") -- static runtime (MSVC) set_runtimes("MD") -- dynamic runtime (MSVC) ``` ### Setting Runtime Path ```lua target("app") set_runtimes("MD") add_rpathdirs("$ORIGIN") -- set relative path lookup ``` ## Target Toolchain Configuration {#configure-target-toolchain} ### Setting Toolchain ```lua target("app") set_toolset("clang") -- use Clang toolchain set_toolset("gcc") -- use GCC toolchain set_toolset("msvc") -- use MSVC toolchain ``` ### Setting Compiler ```lua target("app") set_toolset("cc", "clang") -- set C compiler set_toolset("cxx", "clang++") -- set C++ compiler ``` ## Target Group Configuration {#configure-target-groups} ### Setting Target Groups ```lua target("app") set_group("apps") -- set group to apps target("lib") set_group("libs") -- set group to libs target("test") set_group("tests") -- set group to tests ``` ## Target Default Configuration {#configure-target-defaults} ### Setting Default Targets ```lua target("app") set_default(true) -- set as default build target target("test") set_default(false) -- not set as default build target ``` ### Enabling/Disabling Targets ```lua target("app") set_enabled(true) -- enable target target("old") set_enabled(false) -- disable target ``` ## More Information {#more-information} * Complete API documentation: [Project Target API](/api/description/project-target) * Target instance interfaces: [Target Instance API](/api/scripts/target-instance) * Built-in rules reference: [Built-in Rules](/api/description/builtin-rules) --- --- url: /api/scripts/extension-modules/core/base/bit.md --- # core.base.bit This module provides bitwise operations for Lua scripts in Xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.bit")` ::: ## Overview Although Lua 5.4+ natively supports bitwise operators, Xmake can be configured to use LuaJIT as the runtime. To ensure compatibility across different Lua versions, Xmake provides the `bit` module for more generic bitwise operations. This module provides a common set of bitwise operations that work consistently regardless of whether you're using standard Lua or LuaJIT. ## bit.band * Bitwise AND operation #### Function Prototype ::: tip API ```lua bit.band(a: , b: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | a | Required. First number | | b | Required. Second number | #### Return Value | Type | Description | |------|-------------| | number | Returns the bitwise AND result of a and b | #### Usage ```lua import("core.base.bit") local result = bit.band(5, 3) -- Returns 1 ``` ## bit.bor * Bitwise OR operation #### Function Prototype ::: tip API ```lua bit.bor(a: , b: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | a | Required. First number | | b | Required. Second number | #### Return Value | Type | Description | |------|-------------| | number | Returns the bitwise OR result of a and b | #### Usage ```lua import("core.base.bit") local result = bit.bor(5, 3) -- Returns 7 ``` ## bit.bxor * Bitwise XOR operation #### Function Prototype ::: tip API ```lua bit.bxor(a: , b: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | a | Required. First number | | b | Required. Second number | #### Return Value | Type | Description | |------|-------------| | number | Returns the bitwise XOR result of a and b | #### Usage ```lua import("core.base.bit") local result = bit.bxor(5, 3) -- Returns 6 ``` ## bit.bnot * Bitwise NOT operation #### Function Prototype ::: tip API ```lua bit.bnot(a: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | a | Required. Number to negate | #### Return Value | Type | Description | |------|-------------| | number | Returns the bitwise NOT result of a | #### Usage ```lua import("core.base.bit") local result = bit.bnot(5) -- Returns the bitwise NOT of 5 ``` ## bit.lshift * Left shift operation #### Function Prototype ::: tip API ```lua bit.lshift(a: , b: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | a | Required. Number to shift | | b | Required. Number of bits to shift left | #### Return Value | Type | Description | |------|-------------| | number | Returns a shifted left by b bits | #### Usage ```lua import("core.base.bit") local result = bit.lshift(5, 2) -- Returns 20 (5 << 2) ``` ## bit.rshift * Right shift operation #### Function Prototype ::: tip API ```lua bit.rshift(a: , b: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | a | Required. Number to shift | | b | Required. Number of bits to shift right | #### Return Value | Type | Description | |------|-------------| | number | Returns a shifted right by b bits | #### Usage ```lua import("core.base.bit") local result = bit.rshift(20, 2) -- Returns 5 (20 >> 2) ``` ## bit.tobit * Convert to 32-bit integer #### Function Prototype ::: tip API ```lua bit.tobit(x: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | x | Required. Number to convert | #### Return Value | Type | Description | |------|-------------| | number | Returns x masked to 32-bit integer (0xffffffff) | #### Usage ```lua import("core.base.bit") local result = bit.tobit(0x12345678) -- Returns value masked to 32 bits ``` ## bit.tohex * Convert number to hexadecimal string #### Function Prototype ::: tip API ```lua bit.tohex(x: , n?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | x | Required. Number to convert | | n | Optional. Number of hexadecimal digits (default: 8). Use negative for uppercase | #### Return Value | Type | Description | |------|-------------| | string | Returns hexadecimal string representation | #### Usage ```lua import("core.base.bit") local hex = bit.tohex(255, 2) -- Returns "ff" local hex = bit.tohex(255, -2) -- Returns "FF" local hex = bit.tohex(255) -- Returns "000000ff" ``` --- --- url: /zh/api/scripts/extension-modules/core/base/bit.md --- # core.base.bit 此模块为 Xmake 中的 Lua 脚本提供位运算操作。 ::: tip 提示 使用此模块需要先导入:`import("core.base.bit")` ::: ## 概述 尽管 Lua 5.4+ 原生支持位运算操作符,但 xmake 还可以切换到 luajit 作为 runtime。为了兼容性,这边提供了 bit 模块,提供更通用的位运算操作。 此模块提供了一组通用的位运算操作,无论在标准 Lua 还是 LuaJIT 环境下都能保持一致的工作。 ## bit.band * 位与运算 #### 函数原型 ::: tip API ```lua bit.band(a: , b: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | a | 必需。第一个数字 | | b | 必需。第二个数字 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 a 和 b 的位与结果 | #### 用法说明 ```lua import("core.base.bit") local result = bit.band(5, 3) -- 返回 1 ``` ## bit.bor * 位或运算 #### 函数原型 ::: tip API ```lua bit.bor(a: , b: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | a | 必需。第一个数字 | | b | 必需。第二个数字 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 a 和 b 的位或结果 | #### 用法说明 ```lua import("core.base.bit") local result = bit.bor(5, 3) -- 返回 7 ``` ## bit.bxor * 位异或运算 #### 函数原型 ::: tip API ```lua bit.bxor(a: , b: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | a | 必需。第一个数字 | | b | 必需。第二个数字 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 a 和 b 的位异或结果 | #### 用法说明 ```lua import("core.base.bit") local result = bit.bxor(5, 3) -- 返回 6 ``` ## bit.bnot * 位非运算 #### 函数原型 ::: tip API ```lua bit.bnot(a: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | a | 必需。要取反的数字 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 a 的位非结果 | #### 用法说明 ```lua import("core.base.bit") local result = bit.bnot(5) -- 返回 5 的位非结果 ``` ## bit.lshift * 左移运算 #### 函数原型 ::: tip API ```lua bit.lshift(a: , b: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | a | 必需。要移动的数字 | | b | 必需。左移的位数 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 a 左移 b 位的结果 | #### 用法说明 ```lua import("core.base.bit") local result = bit.lshift(5, 2) -- 返回 20 (5 << 2) ``` ## bit.rshift * 右移运算 #### 函数原型 ::: tip API ```lua bit.rshift(a: , b: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | a | 必需。要移动的数字 | | b | 必需。右移的位数 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 a 右移 b 位的结果 | #### 用法说明 ```lua import("core.base.bit") local result = bit.rshift(20, 2) -- 返回 5 (20 >> 2) ``` ## bit.tobit * 转换为 32 位整数 #### 函数原型 ::: tip API ```lua bit.tobit(x: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | x | 必需。要转换的数字 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 x 掩码为 32 位整数后的值 (0xffffffff) | #### 用法说明 ```lua import("core.base.bit") local result = bit.tobit(0x12345678) -- 返回掩码为 32 位后的值 ``` ## bit.tohex * 转换为十六进制字符串 #### 函数原型 ::: tip API ```lua bit.tohex(x: , n?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | x | 必需。要转换的数字 | | n | 可选。十六进制位数(默认: 8)。使用负数表示大写 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回十六进制字符串表示 | #### 用法说明 ```lua import("core.base.bit") local hex = bit.tohex(255, 2) -- 返回 "ff" local hex = bit.tohex(255, -2) -- 返回 "FF" local hex = bit.tohex(255) -- 返回 "000000ff" ``` --- --- url: /api/scripts/extension-modules/core/base/bloom_filter.md --- # core.base.bloom\_filter This module provides a Bloom filter implementation, a probabilistic data structure used to test whether an element is a member of a set. ::: tip TIP To use this module, you need to import it first: `import("core.base.bloom_filter")` ::: A Bloom filter is a space-efficient probabilistic data structure that is used to test whether an element is a member of a set. False positive matches are possible, but false negatives are not. This makes it useful for checking membership with high probability and minimal memory usage. ## bloom\_filter.new * Create a new Bloom filter #### Function Prototype ::: tip API ```lua bloom_filter.new(opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Configuration options:- `probability` - False positive probability (default: 0.001)- `hash_count` - Number of hash functions (default: 3)- `item_maxn` - Maximum number of items (default: 1000000) | #### Return Value | Type | Description | |------|-------------| | bloom\_filter | Returns a bloom filter instance | | nil, string | Returns nil and error message on failure | #### Usage ```lua import("core.base.bloom_filter") -- Create a new bloom filter with default settings local filter = bloom_filter.new() -- Create with custom settings local filter = bloom_filter.new({ probability = 0.001, hash_count = 3, item_maxn = 1000000 }) ``` ::: tip TIP Supported probability values: 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001 ::: ## filter:set * Add an item to the Bloom filter #### Function Prototype ::: tip API ```lua filter:set(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. String item to add to the filter | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if item was successfully added, false if it already exists | #### Usage ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() -- Add items to the filter if filter:set("hello") then print("Item added successfully") end -- Note: false positives are possible if filter:set("hello") then print("This won't print - item already exists") else print("Item already exists (may be false positive)") end ``` ## filter:get * Check if an item exists in the Bloom filter #### Function Prototype ::: tip API ```lua filter:get(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. String item to check | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if item exists, false otherwise | #### Usage ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() -- Add some items filter:set("hello") filter:set("xmake") -- Check for items if filter:get("hello") then print("hello exists") end if filter:get("not exists") then print("This won't print") else print("Item not found") end ``` ::: warning NOTE Bloom filters can produce false positives (claiming an item exists when it doesn't), but never false negatives (claiming an item doesn't exist when it does). ::: ## filter:data * Get the Bloom filter data as a bytes object #### Function Prototype ::: tip API ```lua filter:data() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | bytes | Returns the filter data as a bytes object | | nil, string | Returns nil and error message on failure | #### Usage ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() filter:set("hello") filter:set("xmake") -- Get the filter data local data = filter:data() -- Save or transfer the data print("Filter size:", data:size()) ``` ## filter:data\_set * Set the Bloom filter data from a bytes object #### Function Prototype ::: tip API ```lua filter:data_set(data: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Bytes object containing the filter data | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if data was set successfully | #### Usage ```lua import("core.base.bloom_filter") -- Create first filter and add items local filter1 = bloom_filter.new() filter1:set("hello") filter1:set("xmake") -- Get data from first filter local data = filter1:data() -- Create second filter and load data local filter2 = bloom_filter.new() filter2:data_set(data) -- Both filters now contain the same items assert(filter2:get("hello") == true) assert(filter2:get("xmake") == true) ``` ## filter:clear * Clear all items from the Bloom filter #### Function Prototype ::: tip API ```lua filter:clear() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() filter:set("hello") filter:set("xmake") -- Clear all items filter:clear() -- Items are now removed assert(filter:get("hello") == false) assert(filter:get("xmake") == false) ``` ## filter:cdata * Get the internal C data handle #### Function Prototype ::: tip API ```lua filter:cdata() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | userdata | Returns the internal C data handle | #### Usage ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() local cdata = filter:cdata() print("C data handle:", cdata) ``` --- --- url: /zh/api/scripts/extension-modules/core/base/bloom_filter.md --- # core.base.bloom\_filter 此模块提供布隆过滤器实现,一种用于测试元素是否是集合成员的概率性数据结构。 ::: tip 提示 使用此模块需要先导入:`import("core.base.bloom_filter")` ::: 布隆过滤器是一种空间高效的概率性数据结构,用于测试元素是否是集合的成员。可能出现误报匹配,但不会出现漏报。这使得它在高概率检查和最小内存使用方面非常有用。 ## bloom\_filter.new * 创建新的布隆过滤器 #### 函数原型 ::: tip API ```lua bloom_filter.new(opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。配置选项:- `probability` - 误报概率(默认: 0.001)- `hash_count` - 哈希函数数量(默认: 3)- `item_maxn` - 最大元素数量(默认: 1000000) | #### 返回值说明 | 类型 | 描述 | |------|------| | bloom\_filter | 返回布隆过滤器实例 | | nil, string | 失败时返回 nil 和错误消息 | #### 用法说明 ```lua import("core.base.bloom_filter") -- 使用默认设置创建新的布隆过滤器 local filter = bloom_filter.new() -- 使用自定义设置创建 local filter = bloom_filter.new({ probability = 0.001, hash_count = 3, item_maxn = 1000000 }) ``` ::: tip 提示 支持的误报概率值: 0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001 ::: ## filter:set * 向布隆过滤器添加元素 #### 函数原型 ::: tip API ```lua filter:set(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 必需。要添加到过滤器中的字符串元素 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 元素添加成功返回 true,如果已经存在返回 false | #### 用法说明 ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() -- 向过滤器添加元素 if filter:set("hello") then print("元素添加成功") end -- 注意:可能出现误报 if filter:set("hello") then print("这不会打印 - 元素已存在") else print("元素已存在(可能是误报)") end ``` ## filter:get * 检查元素是否存在于布隆过滤器中 #### 函数原型 ::: tip API ```lua filter:get(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 必需。要检查的字符串元素 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 元素存在返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() -- 添加一些元素 filter:set("hello") filter:set("xmake") -- 检查元素 if filter:get("hello") then print("hello 存在") end if filter:get("not exists") then print("这不会打印") else print("元素未找到") end ``` ::: warning 注意 布隆过滤器可能出现误报(声称元素存在但实际上不存在),但绝不会出现漏报(声称元素不存在但实际上存在)。 ::: ## filter:data * 获取布隆过滤器数据作为 bytes 对象 #### 函数原型 ::: tip API ```lua filter:data() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | bytes | 返回过滤器的 bytes 对象数据 | | nil, string | 失败时返回 nil 和错误消息 | #### 用法说明 ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() filter:set("hello") filter:set("xmake") -- 获取过滤器数据 local data = filter:data() -- 保存或传输数据 print("过滤器大小:", data:size()) ``` ## filter:data\_set * 从 bytes 对象设置布隆过滤器数据 #### 函数原型 ::: tip API ```lua filter:data_set(data: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。包含过滤器数据的 bytes 对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 数据设置成功返回 true | #### 用法说明 ```lua import("core.base.bloom_filter") -- 创建第一个过滤器并添加元素 local filter1 = bloom_filter.new() filter1:set("hello") filter1:set("xmake") -- 从第一个过滤器获取数据 local data = filter1:data() -- 创建第二个过滤器并加载数据 local filter2 = bloom_filter.new() filter2:data_set(data) -- 两个过滤器现在包含相同的元素 assert(filter2:get("hello") == true) assert(filter2:get("xmake") == true) ``` ## filter:clear * 清除布隆过滤器中的所有元素 #### 函数原型 ::: tip API ```lua filter:clear() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 成功返回 true,失败返回 false | #### 用法说明 ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() filter:set("hello") filter:set("xmake") -- 清除所有元素 filter:clear() -- 元素现在已移除 assert(filter:get("hello") == false) assert(filter:get("xmake") == false) ``` ## filter:cdata * 获取内部 C 数据句柄 #### 函数原型 ::: tip API ```lua filter:cdata() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | userdata | 返回内部 C 数据句柄 | #### 用法说明 ```lua import("core.base.bloom_filter") local filter = bloom_filter.new() local cdata = filter:cdata() print("C 数据句柄:", cdata) ``` --- --- url: /api/scripts/extension-modules/core/base/cpu.md --- # core.base.cpu This module provides CPU information and detection capabilities for the current system. ::: tip TIP To use this module, you need to import it first: `import("core.base.cpu")` ::: This module allows you to query CPU vendor, model, architecture, features, and performance metrics across different platforms (macOS, Linux, Windows, BSD). ## cpu.vendor * Get CPU vendor ID #### Function Prototype ::: tip API ```lua cpu.vendor() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the CPU vendor ID (e.g., "GenuineIntel", "AuthenticAMD") | #### Usage ```lua import("core.base.cpu") local vendor = cpu.vendor() print("CPU Vendor:", vendor) -- Output: "GenuineIntel" or "AuthenticAMD" ``` ## cpu.model * Get CPU model number #### Function Prototype ::: tip API ```lua cpu.model() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns the CPU model number | #### Usage ```lua import("core.base.cpu") local model = cpu.model() print("CPU Model:", model) ``` ## cpu.family * Get CPU family #### Function Prototype ::: tip API ```lua cpu.family() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns the CPU family number | #### Usage ```lua import("core.base.cpu") local family = cpu.family() print("CPU Family:", family) ``` ## cpu.model\_name * Get CPU model name #### Function Prototype ::: tip API ```lua cpu.model_name() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the full CPU model name (e.g., "Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz") | #### Usage ```lua import("core.base.cpu") local name = cpu.model_name() print("CPU Name:", name) ``` ## cpu.features * Get CPU features #### Function Prototype ::: tip API ```lua cpu.features() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns a string containing all CPU features/flags separated by spaces | #### Usage ```lua import("core.base.cpu") local features = cpu.features() print("CPU Features:", features) ``` ## cpu.has\_feature * Check if CPU has a specific feature #### Function Prototype ::: tip API ```lua cpu.has_feature(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Feature name to check (e.g., "sse", "avx", "avx2") | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if the CPU has the feature, false otherwise | #### Usage ```lua import("core.base.cpu") if cpu.has_feature("avx2") then print("AVX2 supported") end if cpu.has_feature("sse") then print("SSE supported") end ``` ## cpu.march * Get CPU micro-architecture #### Function Prototype ::: tip API ```lua cpu.march() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the CPU micro-architecture name (e.g., "Skylake", "Zen 2", "Alder Lake") | #### Usage ```lua import("core.base.cpu") local march = cpu.march() print("CPU Architecture:", march) -- Output: "Skylake", "Zen 2", etc. ``` ## cpu.number * Get number of CPU cores #### Function Prototype ::: tip API ```lua cpu.number() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns the number of CPU cores | #### Usage ```lua import("core.base.cpu") local cores = cpu.number() print("CPU Cores:", cores) ``` ## cpu.usagerate * Get CPU usage rate #### Function Prototype ::: tip API ```lua cpu.usagerate() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns the CPU usage rate as a decimal (0.0 to 1.0, where 1.0 = 100%) | #### Usage ```lua import("core.base.cpu") local usage = cpu.usagerate() print("CPU Usage:", math.floor(usage * 100) .. "%") ``` ## cpu.info * Get all CPU information #### Function Prototype ::: tip API ```lua cpu.info(name?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Optional. Specific field name to retrieve | #### Return Value | Type | Description | |------|-------------| | table | Returns a table containing all CPU information if name is not provided | | any | Returns the specific field value if name is provided | #### Usage ```lua import("core.base.cpu") -- Get all CPU information local info = cpu.info() print("Vendor:", info.vendor) print("Model:", info.model) print("Family:", info.family) print("Architecture:", info.march) print("Cores:", info.ncpu) print("Features:", info.features) print("Usage Rate:", info.usagerate) print("Model Name:", info.model_name) -- Get specific field local vendor = cpu.info("vendor") local cores = cpu.info("ncpu") ``` --- --- url: /zh/api/scripts/extension-modules/core/base/cpu.md --- # core.base.cpu 此模块提供 CPU 信息查询和检测功能。 ::: tip 提示 使用此模块需要先导入:`import("core.base.cpu")` ::: 此模块允许您查询 CPU 厂商、型号、架构、特性以及性能指标,支持跨平台(macOS、Linux、Windows、BSD)。 ## cpu.vendor * 获取 CPU 厂商 ID #### 函数原型 ::: tip API ```lua cpu.vendor() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回 CPU 厂商 ID(例如:"GenuineIntel"、"AuthenticAMD") | #### 用法说明 ```lua import("core.base.cpu") local vendor = cpu.vendor() print("CPU 厂商:", vendor) -- 输出: "GenuineIntel" 或 "AuthenticAMD" ``` ## cpu.model * 获取 CPU 型号编号 #### 函数原型 ::: tip API ```lua cpu.model() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 CPU 型号编号 | #### 用法说明 ```lua import("core.base.cpu") local model = cpu.model() print("CPU 型号:", model) ``` ## cpu.family * 获取 CPU 系列 #### 函数原型 ::: tip API ```lua cpu.family() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 CPU 系列编号 | #### 用法说明 ```lua import("core.base.cpu") local family = cpu.family() print("CPU 系列:", family) ``` ## cpu.model\_name * 获取 CPU 型号名称 #### 函数原型 ::: tip API ```lua cpu.model_name() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回 CPU 完整型号名称(例如:"Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz") | #### 用法说明 ```lua import("core.base.cpu") local name = cpu.model_name() print("CPU 名称:", name) ``` ## cpu.features * 获取 CPU 特性 #### 函数原型 ::: tip API ```lua cpu.features() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回包含所有 CPU 特性/标志的字符串,用空格分隔 | #### 用法说明 ```lua import("core.base.cpu") local features = cpu.features() print("CPU 特性:", features) ``` ## cpu.has\_feature * 检查 CPU 是否具有特定特性 #### 函数原型 ::: tip API ```lua cpu.has_feature(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。要检查的特性名称(例如:"sse"、"avx"、"avx2") | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | CPU 支持该特性返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.cpu") if cpu.has_feature("avx2") then print("支持 AVX2") end if cpu.has_feature("sse") then print("支持 SSE") end ``` ## cpu.march * 获取 CPU 微架构 #### 函数原型 ::: tip API ```lua cpu.march() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回 CPU 微架构名称(例如:"Skylake"、"Zen 2"、"Alder Lake") | #### 用法说明 ```lua import("core.base.cpu") local march = cpu.march() print("CPU 架构:", march) -- 输出: "Skylake"、"Zen 2" 等 ``` ## cpu.number * 获取 CPU 核心数 #### 函数原型 ::: tip API ```lua cpu.number() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 CPU 核心数量 | #### 用法说明 ```lua import("core.base.cpu") local cores = cpu.number() print("CPU 核心数:", cores) ``` ## cpu.usagerate * 获取 CPU 使用率 #### 函数原型 ::: tip API ```lua cpu.usagerate() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回 CPU 使用率,十进制数(0.0 到 1.0,其中 1.0 = 100%) | #### 用法说明 ```lua import("core.base.cpu") local usage = cpu.usagerate() print("CPU 使用率:", math.floor(usage * 100) .. "%") ``` ## cpu.info * 获取所有 CPU 信息 #### 函数原型 ::: tip API ```lua cpu.info(name?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 可选。要获取的特定字段名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 如果未提供 name,返回包含所有 CPU 信息的表格 | | any | 如果提供了 name,返回特定字段的值 | #### 用法说明 ```lua import("core.base.cpu") -- 获取所有 CPU 信息 local info = cpu.info() print("厂商:", info.vendor) print("型号:", info.model) print("系列:", info.family) print("架构:", info.march) print("核心数:", info.ncpu) print("特性:", info.features) print("使用率:", info.usagerate) print("型号名称:", info.model_name) -- 获取特定字段 local vendor = cpu.info("vendor") local cores = cpu.info("ncpu") ``` --- --- url: /api/scripts/extension-modules/core/base/global.md --- # core.base.global Used to get the configuration information of xmake global, that is, the value of the parameter option passed in `xmake g|global --xxx=val`. ## global.get * Get the specified configuration value #### Function Prototype ::: tip API ```lua global.get(key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | #### Usage Similar to [config.get](/api/scripts/extension-modules/core/project/config#config-get), the only difference is that this is obtained from the global configuration. ## global.load * Load configuration #### Function Prototype ::: tip API ```lua global.load() ``` ::: #### Parameter Description No parameters required for this function. #### Usage 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 #### Function Prototype ::: tip API ```lua global.directory() ``` ::: #### Parameter Description No parameters required for this function. #### Usage The default is the `~/.config` directory. ## global.dump * Print out all global configuration information #### Function Prototype ::: tip API ```lua global.dump() ``` ::: #### Parameter Description No parameters required for this function. #### Usage The output is as follows: ```lua { clean = true, ccache = "ccache", xcode_dir = "/Applications/Xcode.app" } ``` --- --- url: /zh/api/scripts/extension-modules/core/base/global.md --- # core.base.global 用于获取xmake全局的配置信息,也就是`xmake g|global --xxx=val` 传入的参数选项值。 ## global.get * 获取指定配置值 #### 函数原型 ::: tip API ```lua global.get(key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | #### 用法说明 类似[config.get](/zh/api/scripts/extension-modules/core/project/config#config-get),唯一的区别就是这个是从全局配置中获取。 ## global.load * 加载配置 #### 函数原型 ::: tip API ```lua global.load() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 类似[global.get](#global-get),唯一的区别就是这个是从全局配置中加载。 ## global.directory * 获取全局配置信息目录 #### 函数原型 ::: tip API ```lua global.directory() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 默认为`~/.config`目录。 ## global.dump * 打印输出所有全局配置信息 #### 函数原型 ::: tip API ```lua global.dump() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 输出结果如下: ```lua { clean = true, ccache = "ccache", xcode_dir = "/Applications/Xcode.app" } ``` --- --- url: /api/scripts/extension-modules/core/base/json.md --- # core.base.json Xmake provides a built-in json module, based on the implementation of lua\_cjson, we can use it to quickly and directly interoperate between json and lua table. We can use `import("core.base.json")` for direct import and use. There are also some examples here: [Json Examples](https://github.com/xmake-io/xmake/blob/master/tests/modules/json/test.lua) ## json.decode * Get the lua table directly from the string decoding json #### Function Prototype ::: tip API ```lua json.decode(jsonstr: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | jsonstr | Required. JSON string to decode | #### Usage Decodes a JSON string into a Lua table. ```lua import("core.base.json") local luatable = json.decode('[1,"2", {"a":1, "b":true}]') print(luatable) ``` ``` { 1.0, "2", { b = true, a = 1.0 } } ``` ::: tip NOTE If there is null in it, you can use `json.null` to judge ::: ## json.encode * Encode a lua table #### Function Prototype ::: tip API ```lua json.encode(t:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | t | Required. Lua table to encode | #### Usage Encodes a Lua table into a JSON string. ```lua local jsonstr = json.encode({1, "2", {a = 1}}) ``` It should be noted that if you need to encode null, you need to use `json.null`, for example ```lua local jsonstr = json.encode({json.null, 1, "2", false, true}) ``` ## json.loadfile * Load the json file directly and parse it into a lua table #### Function Prototype ::: tip API ```lua json.loadfile(filepath: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filepath | Required. Path to the JSON file to load | #### Usage Loads a JSON file and parses it into a Lua table. ```lua local luatable = json.loadfile("/tmp/xxx.json") ``` ## json.savefile * Save the lua table to the specified json file #### Function Prototype ::: tip API ```lua json.savefile(filepath: , data:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filepath | Required. Path to save the JSON file | | data | Required. Lua table to save | #### Usage Saves a Lua table to a JSON file. ```lua json.savefile("/tmp/xxx.json", {1, {a = 1}}) ``` --- --- url: /zh/api/scripts/extension-modules/core/base/json.md --- # core.base.json xmake 提供了内置的 json 模块,基于 lua\_cjson 实现,我们可以用它实现快速的在 json 和 lua table 直接相互操作。 我们可以通过 `import("core.base.json")` 直接导入使用。 这里也有一些例子:[Json Examples](https://github.com/xmake-io/xmake/blob/master/tests/modules/json/test.lua) ## json.decode * 从字符串解码 JSON 获取 Lua table #### 函数原型 ::: tip API ```lua json.decode(jsonstr: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | jsonstr | JSON 字符串 | #### 用法说明 直接从字符串解码 json 获取 lua table. ```lua import("core.base.json") local luatable = json.decode('[1,"2", {"a":1, "b":true}]') print(luatable) ``` ``` { 1.0, "2", { b = true, a = 1.0 } } ``` ::: tip 注意 如果里面有 null,可以用 `json.null` 来判断 ::: ## json.encode * 将 Lua table 编码为 JSON 字符串 #### 函数原型 ::: tip API ```lua json.encode(table:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | table | 要编码的 Lua table | #### 用法说明 我们也可以直接对一个 lua table 进行编码。 ```lua local jsonstr = json.encode({1, "2", {a = 1}}) ``` 需要注意的是,如果需要编码 null,需要使用 `json.null`,例如 ```lua local jsonstr = json.encode({json.null, 1, "2", false, true}) ``` ## json.loadfile * 从文件加载 JSON 并解析为 Lua table #### 函数原型 ::: tip API ```lua json.loadfile(filepath: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | JSON 文件路径 | #### 用法说明 直接加载 json 文件,并解析成 lua table。 ```lua local luatable = json.loadfile("/tmp/xxx.json") ``` ## json.savefile * 将 Lua table 保存为 JSON 文件 #### 函数原型 ::: tip API ```lua json.savefile(filepath: , table:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 要保存的 JSON 文件路径 | | table | 要保存的 Lua table | #### 用法说明 保存 lua table 到指定 json 文件。 ```lua json.savefile("/tmp/xxx.json", {1, {a = 1}}) ``` --- --- url: /api/scripts/extension-modules/core/base/libc.md --- # core.base.libc This module provides low-level C library operations for direct memory manipulation. ::: tip TIP To use this module, you need to import it first: `import("core.base.libc")` ::: This module wraps standard C library functions for memory allocation, copying, and manipulation. It provides consistent interfaces that work with both standard Lua and LuaJIT with FFI. ::: warning WARNING These functions operate at a low level and should be used with caution. Improper use can lead to memory leaks, crashes, or security vulnerabilities. ::: ## libc.malloc * Allocate memory #### Function Prototype ::: tip API ```lua libc.malloc(size: , opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | size | Required. Size of memory to allocate in bytes | | opt | Optional. Options table, supports:- `gc` - If true, memory will be automatically garbage collected | #### Return Value | Type | Description | |------|-------------| | userdata | Returns a pointer to the allocated memory | #### Usage ```lua import("core.base.libc") -- Allocate memory with garbage collection local data = libc.malloc(1024, {gc = true}) -- Allocate memory without auto GC local data = libc.malloc(1024) ``` ## libc.free * Free allocated memory #### Function Prototype ::: tip API ```lua libc.free(data: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Pointer to memory to free | #### Return Value No return value #### Usage ```lua import("core.base.libc") local data = libc.malloc(1024) -- ... use data ... libc.free(data) ``` ## libc.memcpy * Copy memory #### Function Prototype ::: tip API ```lua libc.memcpy(dst: , src: , size: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dst | Required. Destination memory pointer | | src | Required. Source memory pointer | | size | Required. Number of bytes to copy | #### Return Value No return value #### Usage ```lua import("core.base.libc") local src = libc.malloc(100) local dst = libc.malloc(100) libc.memcpy(dst, src, 100) ``` ## libc.memmov * Move memory (handles overlapping regions) #### Function Prototype ::: tip API ```lua libc.memmov(dst: , src: , size: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dst | Required. Destination memory pointer | | src | Required. Source memory pointer | | size | Required. Number of bytes to move | #### Return Value No return value #### Usage ```lua import("core.base.libc") local data = libc.malloc(100) libc.memmov(data, src, 100) ``` ## libc.memset * Set memory to a specific value #### Function Prototype ::: tip API ```lua libc.memset(data: , ch: , size: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Memory pointer to fill | | ch | Required. Byte value to set | | size | Required. Number of bytes to set | #### Return Value No return value #### Usage ```lua import("core.base.libc") local data = libc.malloc(1024) libc.memset(data, 0, 1024) -- Zero-initialize memory ``` ## libc.strndup * Duplicate a string with specified length #### Function Prototype ::: tip API ```lua libc.strndup(s: , n: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | s | Required. Source string | | n | Required. Maximum number of characters to duplicate | #### Return Value | Type | Description | |------|-------------| | string | Returns the duplicated string | #### Usage ```lua import("core.base.libc") local str = "hello world" local dup = libc.strndup(str, 5) -- Returns "hello" ``` ## libc.byteof * Get a byte at a specific offset #### Function Prototype ::: tip API ```lua libc.byteof(data: , offset: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Memory pointer | | offset | Required. Byte offset from start of memory | #### Return Value | Type | Description | |------|-------------| | number | Returns the byte value at the offset | #### Usage ```lua import("core.base.libc") local data = libc.malloc(100) local byte = libc.byteof(data, 10) ``` ## libc.setbyte * Set a byte at a specific offset #### Function Prototype ::: tip API ```lua libc.setbyte(data: , offset: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Memory pointer | | offset | Required. Byte offset from start of memory | | value | Required. Byte value to set | #### Return Value No return value #### Usage ```lua import("core.base.libc") local data = libc.malloc(100) libc.setbyte(data, 10, 255) ``` ## libc.dataptr * Get data pointer #### Function Prototype ::: tip API ```lua libc.dataptr(data: , opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Data to get pointer for | | opt | Optional. Options table, supports:- `ffi` - Use FFI if available (default: true)- `gc` - Enable garbage collection | #### Return Value | Type | Description | |------|-------------| | userdata | Returns a pointer to the data | #### Usage ```lua import("core.base.libc") local ptr = libc.dataptr(data, {ffi = true, gc = true}) ``` ## libc.ptraddr * Get pointer address as a number #### Function Prototype ::: tip API ```lua libc.ptraddr(data: , opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Pointer or data | | opt | Optional. Options table, supports:- `ffi` - Use FFI if available (default: true) | #### Return Value | Type | Description | |------|-------------| | number | Returns the pointer address as a number | #### Usage ```lua import("core.base.libc") local data = libc.malloc(1024) local addr = libc.ptraddr(data) print("Pointer address:", addr) ``` --- --- url: /zh/api/scripts/extension-modules/core/base/libc.md --- # core.base.libc 此模块提供低级 C 库操作,用于直接内存操作。 ::: tip 提示 使用此模块需要先导入:`import("core.base.libc")` ::: 此模块封装了标准 C 库的内存分配、复制和操作函数。它提供了一致的接口,适用于标准 Lua 和使用 FFI 的 LuaJIT。 ::: warning 注意 这些函数在底层操作,应谨慎使用。使用不当可能导致内存泄漏、崩溃或安全隐患。 ::: ## libc.malloc * 分配内存 #### 函数原型 ::: tip API ```lua libc.malloc(size: , opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | size | 必需。要分配的内存大小(字节) | | opt | 可选。选项表格,支持:- `gc` - 如果为 true,内存将自动垃圾回收 | #### 返回值说明 | 类型 | 描述 | |------|------| | userdata | 返回指向分配内存的指针 | #### 用法说明 ```lua import("core.base.libc") -- 分配内存并启用垃圾回收 local data = libc.malloc(1024, {gc = true}) -- 分配内存但不自动回收 local data = libc.malloc(1024) ``` ## libc.free * 释放已分配的内存 #### 函数原型 ::: tip API ```lua libc.free(data: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。要释放的内存指针 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.base.libc") local data = libc.malloc(1024) -- ... 使用 data ... libc.free(data) ``` ## libc.memcpy * 复制内存 #### 函数原型 ::: tip API ```lua libc.memcpy(dst: , src: , size: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dst | 必需。目标内存指针 | | src | 必需。源内存指针 | | size | 必需。要复制的字节数 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.base.libc") local src = libc.malloc(100) local dst = libc.malloc(100) libc.memcpy(dst, src, 100) ``` ## libc.memmov * 移动内存(处理重叠区域) #### 函数原型 ::: tip API ```lua libc.memmov(dst: , src: , size: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dst | 必需。目标内存指针 | | src | 必需。源内存指针 | | size | 必需。要移动的字节数 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.base.libc") local data = libc.malloc(100) libc.memmov(data, src, 100) ``` ## libc.memset * 将内存设置为特定值 #### 函数原型 ::: tip API ```lua libc.memset(data: , ch: , size: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。要填充的内存指针 | | ch | 必需。要设置的字节值 | | size | 必需。要设置的字节数 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.base.libc") local data = libc.malloc(1024) libc.memset(data, 0, 1024) -- 零初始化内存 ``` ## libc.strndup * 复制字符串(指定长度) #### 函数原型 ::: tip API ```lua libc.strndup(s: , n: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | s | 必需。源字符串 | | n | 必需。要复制的最大字符数 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回复制的字符串 | #### 用法说明 ```lua import("core.base.libc") local str = "hello world" local dup = libc.strndup(str, 5) -- 返回 "hello" ``` ## libc.byteof * 获取特定偏移处的字节 #### 函数原型 ::: tip API ```lua libc.byteof(data: , offset: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。内存指针 | | offset | 必需。距内存起始位置的字节偏移 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回偏移处的字节值 | #### 用法说明 ```lua import("core.base.libc") local data = libc.malloc(100) local byte = libc.byteof(data, 10) ``` ## libc.setbyte * 在特定偏移处设置字节 #### 函数原型 ::: tip API ```lua libc.setbyte(data: , offset: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。内存指针 | | offset | 必需。距内存起始位置的字节偏移 | | value | 必需。要设置的字节值 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.base.libc") local data = libc.malloc(100) libc.setbyte(data, 10, 255) ``` ## libc.dataptr * 获取数据指针 #### 函数原型 ::: tip API ```lua libc.dataptr(data: , opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。要获取指针的数据 | | opt | 可选。选项表格,支持:- `ffi` - 如果可用则使用 FFI(默认: true)- `gc` - 启用垃圾回收 | #### 返回值说明 | 类型 | 描述 | |------|------| | userdata | 返回指向数据的指针 | #### 用法说明 ```lua import("core.base.libc") local ptr = libc.dataptr(data, {ffi = true, gc = true}) ``` ## libc.ptraddr * 获取指针地址(作为数字) #### 函数原型 ::: tip API ```lua libc.ptraddr(data: , opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。指针或数据 | | opt | 可选。选项表格,支持:- `ffi` - 如果可用则使用 FFI(默认: true) | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回指针地址(作为数字) | #### 用法说明 ```lua import("core.base.libc") local data = libc.malloc(1024) local addr = libc.ptraddr(data) print("指针地址:", addr) ``` --- --- url: /api/scripts/extension-modules/core/base/option.md --- # core.base.option Commonly used to get the value of the xmake command parameter option, often used for plugin development. ## option.get * Get parameter option values #### Function Prototype ::: tip API ```lua option.get(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Option name | #### Usage Used to get parameter option values in plugin development, for example: ```lua -- import option module import("core.base.option") -- plugin entry function function main(...) print(option.get("info")) end ``` The above code gets the hello plugin and executes: `xmake hello --info=xxxx` The value of the `--info=` option passed in the command, and shows: `xxxx` For task tasks or plugins that are not a main entry, you can use this: ```lua task("hello") on_run(function () import("core.base.option") print(option.get("info")) end) ``` --- --- url: /zh/api/scripts/extension-modules/core/base/option.md --- # core.base.option 一般用于获取xmake命令参数选项的值,常用于插件开发。 ::: tip 提示 使用此模块需要先导入:`import("core.base.option")` ::: ## option.get * 获取参数选项值 #### 函数原型 ::: tip API ```lua option.get(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 选项名称 | #### 用法说明 在插件开发中用于获取参数选项值,例如: ```lua -- 导入选项模块 import("core.base.option") -- 插件入口函数 function main(...) print(option.get("info")) end ``` 上面的代码获取hello插件,执行:`xmake hello --info=xxxx` 命令时候传入的`--info=`选项的值,并显示:`xxxx` 对于非main入口的task任务或插件,可以这么使用: ```lua task("hello") on_run(function () import("core.base.option") print(option.get("info")) end) ``` --- --- url: /api/scripts/extension-modules/core/base/privilege.md --- # core.base.privilege This module provides privilege management for elevated permission operations. ::: tip TIP To use this module, you need to import it first: `import("core.base.privilege")` ::: This module allows you to store and restore privilege levels when working with operations that require elevated permissions, such as writing to system directories. ::: warning WARNING This module should be used with extreme caution. Privilege escalation operations can be dangerous if misused. ::: ## privilege.store * Store current privilege by dropping to original user #### Function Prototype ::: tip API ```lua privilege.store() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if privilege was successfully stored and dropped, false otherwise | #### Usage This function should be called early in the process when you have elevated privileges (e.g., running as root or via sudo). It stores the privilege and drops to the original user that started the process. ```lua import("core.base.privilege") -- When running with elevated privileges, store and drop them -- This allows the process to run with normal permissions by default if privilege.store() then print("Privilege stored, now running as original user") end -- ... later, when privileged operations are needed ... if privilege.get() then -- Perform privileged operations like installing system packages os.vrunv("apt", {"install", "-y", "package-name"}) end ``` ::: warning NOTE This function can only succeed if running as root. It attempts to determine the original user from: 1. SUDO\_UID and SUDO\_GID environment variables (if running via sudo) 2. The owner of the project directory 3. The owner of the current directory ::: ## privilege.has * Check if stored privilege is available #### Function Prototype ::: tip API ```lua privilege.has() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if stored privilege is available, false otherwise | #### Usage ```lua import("core.base.privilege") if privilege.has() then print("Stored privilege is available") else print("No stored privilege") end ``` ## privilege.get * Restore elevated privilege #### Function Prototype ::: tip API ```lua privilege.get() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if privilege was successfully restored, false otherwise | #### Usage This function restores the elevated privileges that were previously stored with `privilege.store()`. ```lua import("core.base.privilege") -- Check if we have stored privilege if privilege.has() then -- Restore privileged access if privilege.get() then print("Privilege restored, now running with elevated permissions") -- Perform privileged operations like installing system packages os.vrunv("apt", {"install", "-y", "package-name"}) -- Or write to protected system directories os.cp("file.txt", "/etc/some/directory/") end end ``` ::: warning NOTE This function can only succeed if `privilege.store()` was previously called successfully. It restores root privileges by setting UID and GID to 0. ::: --- --- url: /zh/api/scripts/extension-modules/core/base/privilege.md --- # core.base.privilege 此模块提供权限管理功能,用于执行需要提升权限的操作。 ::: tip 提示 使用此模块需要先导入:`import("core.base.privilege")` ::: 此模块允许您在需要提升权限的操作(例如写入系统目录)时存储和恢复权限级别。 ::: warning 注意 此模块应谨慎使用。权限提升操作如果使用不当可能是危险的。 ::: ## privilege.store * 存储当前权限并降级到原始用户 #### 函数原型 ::: tip API ```lua privilege.store() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 成功存储并降低权限返回 true,否则返回 false | #### 用法说明 此函数应在进程开始时就调用(当您具有提升权限时,例如以 root 身份运行或通过 sudo)。它存储权限并降级为启动进程的原始用户。 ```lua import("core.base.privilege") -- 在以提升权限运行时,存储并降低权限 -- 这使得进程默认以普通权限运行 if privilege.store() then print("权限已存储,现在以原始用户身份运行") end -- ... 稍后,当需要执行特权操作时 ... if privilege.get() then -- 执行需要提升权限的操作,例如安装系统包 os.vrunv("apt", {"install", "-y", "package-name"}) end ``` ::: warning 注意 此函数只有在以 root 身份运行时才能成功。它尝试从以下方式确定原始用户: 1. SUDO\_UID 和 SUDO\_GID 环境变量(如果通过 sudo 运行) 2. 项目目录的所有者 3. 当前目录的所有者 ::: ## privilege.has * 检查是否已存储权限 #### 函数原型 ::: tip API ```lua privilege.has() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果已存储权限则返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.privilege") if privilege.has() then print("已存储的权限可用") else print("没有已存储的权限") end ``` ## privilege.get * 恢复提升权限 #### 函数原型 ::: tip API ```lua privilege.get() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 成功恢复权限返回 true,否则返回 false | #### 用法说明 此函数恢复之前使用 `privilege.store()` 存储的提升权限。 ```lua import("core.base.privilege") -- 检查是否有已存储的权限 if privilege.has() then -- 恢复提升权限 if privilege.get() then print("权限已恢复,现在以提升权限运行") -- 执行需要提升权限的操作,例如安装系统包 os.vrunv("apt", {"install", "-y", "package-name"}) -- 或者写入受保护的系统目录 os.cp("file.txt", "/etc/some/directory/") end end ``` ::: warning 注意 此函数只有在之前成功调用 `privilege.store()` 时才能成功。它通过将 UID 和 GID 设置为 0 来恢复 root 权限。 ::: --- --- url: /api/scripts/extension-modules/core/base/semver.md --- # core.base.semver A module to work with semantic versionning (semver). It allows you to parse version strings and access various components of a version such as major, minor, ... We can use `import("core.base.semver")` for direct import and use. ## semver.new * Create a new semver instance from a version string. Raise an error if the parsing failed #### Function Prototype ::: tip API ```lua semver.new(version: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Required. Version string | #### Usage Creates a new semver instance from a version string. Raises an error if parsing fails. ```lua local version = semver.new("v2.1.0") ``` ## semver.try\_parse * Same as new, but return a nil value if the parsing failed #### Function Prototype ::: tip API ```lua semver.try_parse(version: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Required. Version string | #### Usage Same as new, but returns nil value if parsing failed. ```lua local version = semver.try_parse("v2.1.0") ``` ## semver.match * Match a valid version from the string #### Function Prototype ::: tip API ```lua semver.match(str: , pos: , pattern: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | str | Required. String to match from | | pos | Required. Start position | | pattern | Optional. Pattern to match | #### Usage Matches a valid version from the string. ```lua print(semver.match("v2.1.0", 1)) -- start from position 1 print(semver.match("v2.1.0", 0, "%d+%.%d+")) ``` ``` 2.1.0 2.1 ``` ## semver.is\_valid * Get if a given string version is valid, by testing if the version can be parsed #### Function Prototype ::: tip API ```lua semver.is_valid(version: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Required. Version string to validate | #### Usage Tests if the version can be parsed. ```lua print(semver.is_valid("536f2bd6a092eba91315b7d1e120dff63392a11d")) print(semver.is_valid("v2.1.0-pre")) ``` ``` false true ``` ## semver.is\_valid\_range * Test if the given range is a valid one #### Function Prototype ::: tip API ```lua semver.is_valid_range(range: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | range | Required. Range string to validate | #### Usage Tests if the given range is a valid one. ```lua print(semver.is_valid_range(">2.1.0")) print(semver.is_valid_range(">v2.1.0")) print(semver.is_valid_range("2.0.0 - <2.1.0")) print(semver.is_valid_range("1.0 || 2.1")) ``` ``` true false false true ``` ## semver.compare * Compare two versions, return a number between 1 and -1 #### Function Prototype ::: tip API ```lua semver.compare(v1: , v2: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | v1 | Required. First version string | | v2 | Required. Second version string | #### Usage Compares two versions and returns a number between -1 and 1. ```lua print(semver.compare("v2.2", "v2.2.0")) print(semver.compare("v2.2.0", "v2.1.0")) print(semver.compare("v2.1.1", "v2.1.0")) print(semver.compare("v2.1.0", "v2.2.0")) ``` ``` 0 1 1 -1 ``` ## semver.satisfies * Check if a version satisfies a range version #### Function Prototype ::: tip API ```lua semver.satisfies(version: , range: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Required. Version string | | range | Required. Range string | #### Usage Checks if a version satisfies a range version. ```lua print(semver.satisfies("v2.1.0", ">= 2.1")) print(semver.satisfies("v2.1.0", ">1.0 <2.1")) print(semver.satisfies("v2.1.0", ">1.0 || <2.1")) print(semver.satisfies("v2.1.0", ">=2.x || 3.x - 4.x")) ``` ``` true false true true ``` ## semver.select * Select required version from versions, tags and branches #### Function Prototype ::: tip API ```lua semver.select(range: , versions:
, tags:
, branches:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | range | Required. Range string | | versions | Required. Array of versions | | tags | Required. Array of tags | | branches | Required. Array of branches | #### Usage Selects required version from versions, tags and branches. ```lua local version, source = semver.select(">=1.5.0 <1.6", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"}) print(semver.select(">=1.5.0 <1.6", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"})) print(semver.select("v1.5.0", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"})) print(semver.select("master", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"})) ``` ``` 1.5.1 version v1.5.0 tag master branch ``` ## semver:get * Get a value from the informations table #### Function Prototype ::: tip API ```lua semver:get(key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Required. Information key | #### Usage Gets a value from the informations table. ```lua local version = semver.new("v2.1.0+build") print(version["_INFO"]) print(version:get("major")) ``` ``` { prerelease = { }, build = { "build" }, version = "v2.1.0+build", raw = "v2.1.0+build", patch = 0, minor = 1, major = 2 } 2 ``` ## semver:major * Get the major version #### Function Prototype ::: tip API ```lua semver:major() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the major version number. ```lua semver.new("v2.1.0"):major() -- return 2 ``` ## semver:minor * Get the minor version #### Function Prototype ::: tip API ```lua semver:minor() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the minor version number. ```lua semver.new("v2.1.0"):minor() -- return 1 ``` ## semver:patch * Get the patch version #### Function Prototype ::: tip API ```lua semver:patch() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the patch version number. ```lua semver.new("v2.1.0"):patch() -- return 0 ``` ## semver:build * Get the build version #### Function Prototype ::: tip API ```lua semver:build() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the build version. ```lua semver.new("v2.1.0+build"):build() -- return {"build"} ``` ## semver:prerelease * Get the prerelease version #### Function Prototype ::: tip API ```lua semver:prerelease() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the prerelease version. ```lua semver.new("v2.1.0-prerelease"):prerelease() -- return {"prerelease"} ``` ## semver:rawstr * Get the raw string #### Function Prototype ::: tip API ```lua semver:rawstr() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the raw version string. ```lua semver.new("v2.1.0+build"):rawstr() -- return v2.1.0+build ``` ## semver:shortstr * Get the short version string #### Function Prototype ::: tip API ```lua semver:shortstr() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the short version string. ```lua semver.new("v2.1.0+build"):shortstr() -- return 2.1.0 ``` --- --- url: /zh/api/scripts/extension-modules/core/base/semver.md --- # core.base.semver 一个用于语义版本控制 (semver) 的模块。它允许您解析版本字符串并访问版本的各个组成部分,例如主版本号、次版本号等。 我们可以使用 `import("core.base.semver")` 直接导入和使用。 ## semver.new * 根据版本字符串创建一个新的 semver 实例 #### 函数原型 ::: tip API ```lua semver.new(version: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 必需。版本字符串 | #### 用法说明 如果解析失败,则抛出错误 ```lua local version = semver.new("v2.1.0") ``` ## semver.try\_parse * 与 new 相同,但如果解析失败则返回 nil 值 #### 函数原型 ::: tip API ```lua semver.try_parse(version: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 必需。版本字符串 | #### 用法说明 与 new 相同,但如果解析失败则返回 nil 值 ```lua local version = semver.try_parse("v2.1.0") ``` ## semver.match * 从字符串中匹配有效版本 #### 函数原型 ::: tip API ```lua semver.match(str: , start: , pattern: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | str | 必需。要匹配的字符串 | | start | 必需。开始位置 | | pattern | 可选。匹配模式 | #### 用法说明 从字符串中匹配有效版本 ```lua print(semver.match("v2.1.0", 1)) -- start from position 1 print(semver.match("v2.1.0", 0, "%d+%.%d+")) ``` ``` 2.1.0 2.1 ``` ## semver.is\_valid * 通过测试版本是否可以解析来获取给定字符串版本是否有效 #### 函数原型 ::: tip API ```lua semver.is_valid(version: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 必需。要验证的版本字符串 | #### 用法说明 通过测试版本是否可以解析来获取给定字符串版本是否有效 ```lua print(semver.is_valid("536f2bd6a092eba91315b7d1e120dff63392a11d")) print(semver.is_valid("v2.1.0-pre")) ``` ``` false true ``` ## semver.is\_valid\_range * 测试给定的范围是否有效 #### 函数原型 ::: tip API ```lua semver.is_valid_range(range: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | range | 必需。要验证的范围字符串 | #### 用法说明 测试给定的范围是否有效 ```lua print(semver.is_valid_range(">2.1.0")) print(semver.is_valid_range(">v2.1.0")) print(semver.is_valid_range("2.0.0 - <2.1.0")) print(semver.is_valid_range("1.0 || 2.1")) ``` ``` true false false true ``` ## semver.compare * 比较两个版本,返回 1 到 -1 之间的数字 #### 函数原型 ::: tip API ```lua semver.compare(version1: , version2: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version1 | 必需。第一个版本字符串 | | version2 | 必需。第二个版本字符串 | #### 用法说明 比较两个版本,返回 1 到 -1 之间的数字 ```lua print(semver.compare("v2.2", "v2.2.0")) print(semver.compare("v2.2.0", "v2.1.0")) print(semver.compare("v2.1.1", "v2.1.0")) print(semver.compare("v2.1.0", "v2.2.0")) ``` ``` 0 1 1 -1 ``` ## semver.satisfies * 检查版本是否满足范围版本 #### 函数原型 ::: tip API ```lua semver.satisfies(version: , range: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 必需。要检查的版本字符串 | | range | 必需。范围字符串 | #### 用法说明 检查版本是否满足范围版本 ```lua print(semver.satisfies("v2.1.0", ">= 2.1")) print(semver.satisfies("v2.1.0", ">1.0 <2.1")) print(semver.satisfies("v2.1.0", ">1.0 || <2.1")) print(semver.satisfies("v2.1.0", ">=2.x || 3.x - 4.x")) ``` ``` true false true true ``` ## semver.select * 从版本、标签和分支中选择所需的版本 #### 函数原型 ::: tip API ```lua semver.select(range: , versions:
, tags:
, branches:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | range | 必需。版本范围字符串 | | versions | 必需。版本数组 | | tags | 必需。标签数组 | | branches | 必需。分支数组 | #### 用法说明 从版本、标签和分支中选择所需的版本 ```lua local version, source = semver.select(">=1.5.0 <1.6", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"}) print(semver.select(">=1.5.0 <1.6", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"})) print(semver.select("v1.5.0", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"})) print(semver.select("master", {"1.5.0", "1.5.1"}, {"v1.5.0"}, {"master", "dev"})) ``` ``` 1.5.1 version v1.5.0 tag master branch ``` ## semver:get * 从信息表中获取一个值 #### 函数原型 ::: tip API ```lua semver:get(key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 必需。要获取的键名 | #### 用法说明 从信息表中获取一个值 ```lua local version = semver.new("v2.1.0+build") print(version["_INFO"]) print(version:get("major")) ``` ``` { prerelease = { }, build = { "build" }, version = "v2.1.0+build", raw = "v2.1.0+build", patch = 0, minor = 1, major = 2 } 2 ``` ## semver:major * 获取主要版本 #### 函数原型 ::: tip API ```lua semver:major() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取主要版本 ```lua semver.new("v2.1.0"):major() -- return 2 ``` ## semver:minor * 获取次要版本 #### 函数原型 ::: tip API ```lua semver:minor() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取次要版本 ```lua semver.new("v2.1.0"):minor() -- return 1 ``` ## semver:patch * 获取补丁版本 #### 函数原型 ::: tip API ```lua semver:patch() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取补丁版本 ```lua semver.new("v2.1.0"):patch() -- return 0 ``` ## semver:build * 获取构建版本 #### 函数原型 ::: tip API ```lua semver:build() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取构建版本 ```lua semver.new("v2.1.0+build"):build() -- return {"build"} ``` ## semver:prerelease * 获取预发布版本 #### 函数原型 ::: tip API ```lua semver:prerelease() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取预发布版本 ```lua semver.new("v2.1.0-prerelease"):prerelease() -- return {"prerelease"} ``` ## semver:rawstr * 获取原始字符串 #### 函数原型 ::: tip API ```lua semver:rawstr() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取原始字符串 ```lua semver.new("v2.1.0+build"):rawstr() -- return v2.1.0+build ``` ## semver:shortstr * 获取短版本字符串 #### 函数原型 ::: tip API ```lua semver:shortstr() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取短版本字符串 ```lua semver.new("v2.1.0+build"):shortstr() -- return 2.1.0 ``` --- --- url: /api/scripts/extension-modules/core/base/task.md --- # core.base.task Used for task operations, generally used to call other task tasks in custom scripts and plug-in tasks. ## task.run * Run the specified task #### Function Prototype ::: tip API ```lua task.run(name: , opt:
, ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Task name | | opt | Optional. Task options table | | ... | Optional. Arguments passed to the task function | #### Usage Used to run tasks or plugins defined by [task](/api/description/plugin-and-task.html#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"}) end ``` --- --- url: /zh/api/scripts/extension-modules/core/base/task.md --- # core.base.task 用于任务操作,一般用于在自定义脚本中、插件任务中,调用运行其他task任务。 ## task.run * 运行指定任务 #### 函数原型 ::: tip API ```lua task.run(name: , opt:
, ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。任务名称 | | opt | 可选。任务选项表 | | ... | 可选。传递给任务函数的参数 | #### 用法说明 用于在自定义脚本、插件任务中运行[task](/zh/api/description/plugin-and-task.html#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 ``` --- --- url: /api/scripts/extension-modules/core/base/thread.md --- # core.base.thread Provides native thread support for concurrent programming, including thread creation, synchronization primitives, and inter-thread communication. ## thread.start * Start a thread #### Function Prototype ::: tip API ```lua thread.start(callback: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | callback | Required. Callback function to execute in the thread | | ... | Optional. Additional arguments passed to the callback function | #### Usage Creates and starts a thread to execute a callback function. Return Value: Returns a thread object that can be used to wait for thread completion ::: tip NOTE Each thread is a separate Lua VM instance, their Lua variable states are completely isolated and cannot be directly shared. Parameters are passed unidirectionally and internally serialized, therefore only support serializable parameters like `string`, `table`, `number`, etc. ::: ## thread.start\_named * Start a named thread #### Function Prototype ::: tip API ```lua thread.start_named(name: , callback: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Thread name | | callback | Required. Callback function to execute in the thread | | ... | Optional. Additional arguments passed to the callback function | #### Usage Creates and starts a new thread with the specified name and callback function. Return Value: Returns a thread object that can be used to wait for thread completion ::: tip NOTE Parameters are passed unidirectionally and internally serialized, therefore only support serializable parameters like `string`, `table`, `number`, etc. ::: ### Example ```lua import("core.base.thread") function callback(id) import("core.base.thread") print("%s: %d starting ..", thread.running(), id) for i = 1, 10 do print("%s: %d", thread.running(), i) os.sleep(1000) end print("%s: %d end", thread.running(), id) end function main() local t0 = thread.start_named("thread_0", callback, 0) local t1 = thread.start_named("thread_1", callback, 1) t0:wait(-1) t1:wait(-1) end ``` ## thread.running * Get the current thread name #### Function Prototype ::: tip API ```lua thread.running() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the name of the currently running thread. Returns the name of the current thread as a string. ## thread.mutex * Create a mutex object #### Function Prototype ::: tip API ```lua thread.mutex() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a new mutex for thread synchronization. Return Value: Returns a mutex object with the following methods: `mutex:lock()` lock the mutex, `mutex:unlock()` unlock the mutex ::: tip NOTE Mutex can be accessed across threads for inter-thread synchronization. ::: ### Example ```lua import("core.base.thread") function callback(mutex) import("core.base.thread") print("%s: starting ..", thread.running()) for i = 1, 10 do mutex:lock() print("%s: %d", thread.running(), i) mutex:unlock() os.sleep(1000) end print("%s: end", thread.running()) end function main() local mutex = thread.mutex() local t0 = thread.start_named("thread_0", callback, mutex) local t1 = thread.start_named("thread_1", callback, mutex) t0:wait(-1) t1:wait(-1) end ``` ## thread.event * Create an event object #### Function Prototype ::: tip API ```lua thread.event() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a new event for thread signaling and synchronization. Return Value: Returns an event object with the following methods: `event:wait(timeout)` wait for the event to be signaled, `event:post()` signal the event ::: tip NOTE Event object can be accessed across threads for inter-thread signaling. ::: ### Example ```lua import("core.base.thread") function callback(event) import("core.base.thread") print("%s: starting ..", thread.running()) while true do print("%s: waiting ..", thread.running()) if event:wait(-1) > 0 then print("%s: triggered", thread.running()) end end end function main() local event = thread.event() local t = thread.start_named("keyboard", callback, event) while true do local ch = io.read() if ch then event:post() end end t:wait(-1) end ``` ## thread.semaphore * Create a semaphore object #### Function Prototype ::: tip API ```lua thread.semaphore(name: , initial_count: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Semaphore name | | initial\_count | Required. Initial count value | #### Usage Creates a new semaphore for thread synchronization and resource counting. Return Value: Returns a semaphore object with the following methods: `semaphore:wait(timeout)` wait for semaphore (decrement count), `semaphore:post(count)` post to semaphore (increment count) ::: tip NOTE Semaphore can be accessed across threads for inter-thread resource counting and synchronization. ::: ### Example ```lua import("core.base.thread") function callback(semaphore) import("core.base.thread") print("%s: starting ..", thread.running()) while true do print("%s: waiting ..", thread.running()) if semaphore:wait(-1) > 0 then print("%s: triggered", thread.running()) end end end function main() local semaphore = thread.semaphore("", 1) local t = thread.start_named("keyboard", callback, semaphore) while true do local ch = io.read() if ch then semaphore:post(2) end end t:wait(-1) end ``` ## thread.queue * Create a thread-safe queue object #### Function Prototype ::: tip API ```lua thread.queue() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a new thread-safe queue for inter-thread data communication. Return Value: Returns a queue object with the following methods: `queue:push(value)` push a value to the queue, `queue:pop()` pop a value from the queue, `queue:empty()` check if the queue is empty ::: tip NOTE Queue is the primary way for inter-thread data communication and supports cross-thread access. ::: ### Example ```lua import("core.base.thread") function callback(event, queue) print("starting ..") while true do print("waiting ..") if event:wait(-1) > 0 then while not queue:empty() do print(" -> %s", queue:pop()) end end end end function main() local event = thread.event() local queue = thread.queue() local t = thread.start_named("", callback, event, queue) while true do local ch = io.read() if ch then queue:push(ch) event:post() end end t:wait(-1) end ``` ## thread.sharedata * Create a shared data object #### Function Prototype ::: tip API ```lua thread.sharedata() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a new shared data object for inter-thread data sharing. Return Value: Returns a shared data object with the following methods: `sharedata:set(value)` set the shared data value, `sharedata:get()` get the shared data value ::: tip NOTE Shared data object is the primary way for inter-thread data sharing and supports cross-thread access. ::: ### Example ```lua import("core.base.thread") function callback(event, sharedata) print("starting ..") while true do print("waiting ..") if event:wait(-1) > 0 then print(" -> %s", sharedata:get()) end end end function main() local event = thread.event() local sharedata = thread.sharedata() local t = thread.start_named("", callback, event, sharedata) while true do local ch = io.read() if ch then sharedata:set(ch) event:post() end end t:wait(-1) end ``` ## thread:wait * Wait for thread completion (thread instance method) #### Function Prototype ::: tip API ```lua thread:wait(timeout: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | timeout | Required. Timeout in milliseconds (-1 for infinite wait) | #### Usage Waits for the thread to complete execution. This method supports mixed scheduling with coroutines, allowing you to wait for thread completion within a coroutine. Return Value: Returns a status code indicating the wait result ### Example (Mixed Thread and Coroutine Scheduling) ```lua import("core.base.thread") import("core.base.scheduler") function thread_loop() import("core.base.thread") print("%s: starting ..", thread.running()) for i = 1, 10 do print("%s: %d", thread.running(), i) os.sleep(1000) end print("%s: end", thread.running()) end function coroutine_loop() print("%s: starting ..", scheduler.co_running()) for i = 1, 10 do print("%s: %d", scheduler.co_running(), i) os.sleep(1000) end print("%s: end", scheduler.co_running()) end function main() scheduler.co_start_named("coroutine", coroutine_loop) local t = thread.start_named("thread", thread_loop) t:wait(-1) -- Wait for thread completion in coroutine end ``` --- --- url: /zh/api/scripts/extension-modules/core/base/thread.md --- # core.base.thread 提供原生线程支持,用于并发编程,包括线程创建、同步原语和线程间通信。 ## thread.start * 启动线程 #### 函数原型 ::: tip API ```lua thread.start(callback: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | callback | 必需。在线程中执行的回调函数 | | ... | 可选。传递给回调函数的额外参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | thread | 返回一个线程对象,可用于等待线程完成 | #### 用法说明 创建并启动一个线程执行回调函数。 ::: tip 注意 每个线程都是单独的 Lua VM 实例,它们的 Lua 变量状态是完全隔离的,不能直接共享。参数传入是单向的,内部通过序列化方式传入,因此只支持 `string`、`table`、`number` 等支持序列化的参数。 ::: ## thread.start\_named * 启动命名线程 #### 函数原型 ::: tip API ```lua thread.start_named(name: , callback: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。线程名称 | | callback | 必需。在线程中执行的回调函数 | | ... | 可选。传递给回调函数的额外参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | thread | 返回一个线程对象,可用于等待线程完成 | #### 用法说明 创建并启动一个具有指定名称和回调函数的新线程。 ::: tip 注意 参数传入是单向的,内部通过序列化方式传入,因此只支持 `string`、`table`、`number` 等支持序列化的参数。 ::: 示例: ```lua import("core.base.thread") function callback(id) import("core.base.thread") print("%s: %d starting ..", thread.running(), id) for i = 1, 10 do print("%s: %d", thread.running(), i) os.sleep(1000) end print("%s: %d end", thread.running(), id) end function main() local t0 = thread.start_named("thread_0", callback, 0) local t1 = thread.start_named("thread_1", callback, 1) t0:wait(-1) t1:wait(-1) end ``` ## thread.running * 获取当前线程名称 #### 函数原型 ::: tip API ```lua thread.running() ``` ::: #### 参数说明 无参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回当前线程的名称字符串 | #### 用法说明 返回当前运行线程的名称。 ## thread.mutex * 创建互斥锁对象 #### 函数原型 ::: tip API ```lua thread.mutex() ``` ::: #### 参数说明 无参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | mutex | 返回一个互斥锁对象,具有以下方法:mutex:lock() 锁定互斥锁,mutex:unlock() 解锁互斥锁 | #### 用法说明 创建一个新的互斥锁用于线程同步。 ::: tip 注意 互斥锁可以跨线程访问,用于线程间同步。 ::: 示例: ```lua import("core.base.thread") function callback(mutex) import("core.base.thread") print("%s: starting ..", thread.running()) for i = 1, 10 do mutex:lock() print("%s: %d", thread.running(), i) mutex:unlock() os.sleep(1000) end print("%s: end", thread.running()) end function main() local mutex = thread.mutex() local t0 = thread.start_named("thread_0", callback, mutex) local t1 = thread.start_named("thread_1", callback, mutex) t0:wait(-1) t1:wait(-1) end ``` ## thread.event * 创建事件对象 #### 函数原型 ::: tip API ```lua thread.event() ``` ::: #### 参数说明 无参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | event | 返回一个事件对象,具有以下方法:event:wait(timeout) 等待事件信号,event:post() 发送事件信号 | #### 用法说明 创建一个新的事件用于线程信号和同步。 ::: tip 注意 事件对象可以跨线程访问,用于线程间信号通信。 ::: 示例: ```lua import("core.base.thread") function callback(event) import("core.base.thread") print("%s: starting ..", thread.running()) while true do print("%s: waiting ..", thread.running()) if event:wait(-1) > 0 then print("%s: triggered", thread.running()) end end end function main() local event = thread.event() local t = thread.start_named("keyboard", callback, event) while true do local ch = io.read() if ch then event:post() end end t:wait(-1) end ``` ## thread.semaphore * 创建信号量对象 #### 函数原型 ::: tip API ```lua thread.semaphore(name: , initial_count: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。信号量名称 | | initial\_count | 必需。初始计数值 | #### 返回值说明 | 类型 | 描述 | |------|------| | semaphore | 返回一个信号量对象,具有以下方法:semaphore:wait(timeout) 等待信号量(减少计数),semaphore:post(count) 发送信号量(增加计数) | #### 用法说明 创建一个新的信号量用于线程同步和资源计数。 ::: tip 注意 信号量可以跨线程访问,用于线程间资源计数和同步。 ::: 示例: ```lua import("core.base.thread") function callback(semaphore) import("core.base.thread") print("%s: starting ..", thread.running()) while true do print("%s: waiting ..", thread.running()) if semaphore:wait(-1) > 0 then print("%s: triggered", thread.running()) end end end function main() local semaphore = thread.semaphore("", 1) local t = thread.start_named("keyboard", callback, semaphore) while true do local ch = io.read() if ch then semaphore:post(2) end end t:wait(-1) end ``` ## thread.queue * 创建线程安全队列对象 #### 函数原型 ::: tip API ```lua thread.queue() ``` ::: #### 参数说明 无参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | queue | 返回一个队列对象,具有以下方法:queue:push(value) 向队列推送值,queue:pop() 从队列弹出值,queue:empty() 检查队列是否为空 | #### 用法说明 创建一个新的线程安全队列用于线程间数据通信。 ::: tip 注意 队列是线程间数据通信的主要方式,支持跨线程访问。 ::: 示例: ```lua import("core.base.thread") function callback(event, queue) print("starting ..") while true do print("waiting ..") if event:wait(-1) > 0 then while not queue:empty() do print(" -> %s", queue:pop()) end end end end function main() local event = thread.event() local queue = thread.queue() local t = thread.start_named("", callback, event, queue) while true do local ch = io.read() if ch then queue:push(ch) event:post() end end t:wait(-1) end ``` ## thread.sharedata * 创建共享数据对象 #### 函数原型 ::: tip API ```lua thread.sharedata() ``` ::: #### 参数说明 无参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | sharedata | 返回一个共享数据对象,具有以下方法:sharedata:set(value) 设置共享数据值,sharedata:get() 获取共享数据值 | #### 用法说明 创建一个新的共享数据对象用于线程间数据共享。 ::: tip 注意 共享数据对象是线程间数据共享的主要方式,支持跨线程访问。 ::: 示例: ```lua import("core.base.thread") function callback(event, sharedata) print("starting ..") while true do print("waiting ..") if event:wait(-1) > 0 then print(" -> %s", sharedata:get()) end end end function main() local event = thread.event() local sharedata = thread.sharedata() local t = thread.start_named("", callback, event, sharedata) while true do local ch = io.read() if ch then sharedata:set(ch) event:post() end end t:wait(-1) end ``` ## thread:wait * 等待线程完成(线程实例方法) #### 函数原型 ::: tip API ```lua thread:wait(timeout: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | timeout | 必需。超时时间(毫秒),-1表示无限等待 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回表示等待结果的状态码 | #### 用法说明 等待线程完成执行。此方法支持与协程混合调度,可以在协程中等待线程完成。 示例(线程与协程混合调度): ```lua import("core.base.thread") import("core.base.scheduler") function thread_loop() import("core.base.thread") print("%s: starting ..", thread.running()) for i = 1, 10 do print("%s: %d", thread.running(), i) os.sleep(1000) end print("%s: end", thread.running()) end function coroutine_loop() print("%s: starting ..", scheduler.co_running()) for i = 1, 10 do print("%s: %d", scheduler.co_running(), i) os.sleep(1000) end print("%s: end", scheduler.co_running()) end function main() scheduler.co_start_named("coroutine", coroutine_loop) local t = thread.start_named("thread", thread_loop) t:wait(-1) -- 在协程中等待线程完成 end ``` --- --- url: /api/scripts/extension-modules/core/base/tty.md --- # core.base.tty This module provides terminal control and detection capabilities for terminal operations. ::: tip TIP To use this module, you need to import it first: `import("core.base.tty")` ::: This module provides functions for terminal control sequences, color support detection, and terminal information querying. It supports VT-ANSI sequences for advanced terminal manipulation. ## tty.erase\_line\_to\_end * Erase from current cursor position to the end of the current line #### Function Prototype ::: tip API ```lua tty.erase_line_to_end() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.erase_line_to_end() ``` ## tty.erase\_line\_to\_start * Erase from current cursor position to the start of the current line #### Function Prototype ::: tip API ```lua tty.erase_line_to_start() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.erase_line_to_start() ``` ## tty.erase\_line * Erase the entire current line #### Function Prototype ::: tip API ```lua tty.erase_line() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.erase_line() ``` ## tty.erase\_down * Erase from current line down to the bottom of the screen #### Function Prototype ::: tip API ```lua tty.erase_down() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.erase_down() ``` ## tty.erase\_up * Erase from current line up to the top of the screen #### Function Prototype ::: tip API ```lua tty.erase_up() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.erase_up() ``` ## tty.erase\_screen * Erase the entire screen and move cursor to home position #### Function Prototype ::: tip API ```lua tty.erase_screen() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.erase_screen() ``` ## tty.cursor\_save * Save current cursor position #### Function Prototype ::: tip API ```lua tty.cursor_save() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.cursor_save() ``` ## tty.cursor\_restore * Restore saved cursor position #### Function Prototype ::: tip API ```lua tty.cursor_restore() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") -- Save cursor position tty.cursor_save() -- ... do something ... -- Restore cursor position tty.cursor_restore() ``` ## tty.cursor\_and\_attrs\_save * Save current cursor position and color attributes #### Function Prototype ::: tip API ```lua tty.cursor_and_attrs_save() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.cursor_and_attrs_save() ``` ## tty.cursor\_and\_attrs\_restore * Restore saved cursor position and color attributes #### Function Prototype ::: tip API ```lua tty.cursor_and_attrs_restore() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") -- Save cursor and attributes tty.cursor_and_attrs_save() -- ... change colors, do something ... -- Restore cursor position and colors tty.cursor_and_attrs_restore() ``` ## tty.cr * Carriage return #### Function Prototype ::: tip API ```lua tty.cr() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.cr() ``` ## tty.flush * Flush terminal output #### Function Prototype ::: tip API ```lua tty.flush() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | tty | Returns the tty module for method chaining | #### Usage ```lua import("core.base.tty") tty.flush() ``` ## tty.shell * Get shell name #### Function Prototype ::: tip API ```lua tty.shell() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the shell name (e.g., "bash", "zsh", "pwsh", "powershell", "cmd") | #### Usage ```lua import("core.base.tty") local shell = tty.shell() print("Current shell:", shell) -- Output: "bash", "zsh", etc. ``` ## tty.term * Get terminal name #### Function Prototype ::: tip API ```lua tty.term() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the terminal name (e.g., "xterm", "vscode", "windows-terminal", "mintty") | #### Usage ```lua import("core.base.tty") local term = tty.term() print("Terminal:", term) -- Output: "xterm", "vscode", etc. ``` ## tty.has\_emoji * Check if terminal supports emoji #### Function Prototype ::: tip API ```lua tty.has_emoji() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if emoji is supported, false otherwise | #### Usage ```lua import("core.base.tty") if tty.has_emoji() then print("Emoji supported!") else print("No emoji support") end ``` ## tty.has\_vtansi * Check if terminal supports VT-ANSI sequences #### Function Prototype ::: tip API ```lua tty.has_vtansi() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if VT-ANSI is supported, false otherwise | #### Usage ```lua import("core.base.tty") if tty.has_vtansi() then -- Use advanced terminal features tty.erase_screen() end ``` ## tty.has\_color8 * Check if terminal supports 8 colors #### Function Prototype ::: tip API ```lua tty.has_color8() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if 8 colors are supported, false otherwise | #### Usage ```lua import("core.base.tty") if tty.has_color8() then -- Use 8-color escape sequences end ``` ## tty.has\_color256 * Check if terminal supports 256 colors #### Function Prototype ::: tip API ```lua tty.has_color256() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if 256 colors are supported, false otherwise | #### Usage ```lua import("core.base.tty") if tty.has_color256() then -- Use 256-color escape sequences end ``` ## tty.has\_color24 * Check if terminal supports 24-bit true color #### Function Prototype ::: tip API ```lua tty.has_color24() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if 24-bit color is supported, false otherwise | #### Usage ```lua import("core.base.tty") if tty.has_color24() then -- Use 24-bit true color escape sequences end ``` ## tty.term\_mode * Get or set terminal mode #### Function Prototype ::: tip API ```lua tty.term_mode(stdtype: , newmode?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | stdtype | Required. Standard stream type ("stdin", "stdout", "stderr") | | newmode | Optional. New terminal mode to set | #### Return Value | Type | Description | |------|-------------| | number | Returns the old terminal mode | #### Usage ```lua import("core.base.tty") -- Get current mode local oldmode = tty.term_mode("stdout") -- Set new mode tty.term_mode("stdout", newmode) ``` --- --- url: /zh/api/scripts/extension-modules/core/base/tty.md --- # core.base.tty 此模块提供终端控制和检测功能。 ::: tip 提示 使用此模块需要先导入:`import("core.base.tty")` ::: 此模块提供终端控制序列、颜色支持检测和终端信息查询功能。支持 VT-ANSI 序列以实现高级终端操作。 ## tty.erase\_line\_to\_end * 清除从当前光标位置到行尾的内容 #### 函数原型 ::: tip API ```lua tty.erase_line_to_end() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.erase_line_to_end() ``` ## tty.erase\_line\_to\_start * 清除从当前光标位置到行首的内容 #### 函数原型 ::: tip API ```lua tty.erase_line_to_start() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.erase_line_to_start() ``` ## tty.erase\_line * 清除整行 #### 函数原型 ::: tip API ```lua tty.erase_line() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.erase_line() ``` ## tty.erase\_down * 清除从当前行到屏幕底部 #### 函数原型 ::: tip API ```lua tty.erase_down() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.erase_down() ``` ## tty.erase\_up * 清除从当前行到屏幕顶部 #### 函数原型 ::: tip API ```lua tty.erase_up() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.erase_up() ``` ## tty.erase\_screen * 清除整个屏幕并将光标移到起始位置 #### 函数原型 ::: tip API ```lua tty.erase_screen() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.erase_screen() ``` ## tty.cursor\_save * 保存当前光标位置 #### 函数原型 ::: tip API ```lua tty.cursor_save() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.cursor_save() ``` ## tty.cursor\_restore * 恢复保存的光标位置 #### 函数原型 ::: tip API ```lua tty.cursor_restore() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") -- 保存光标位置 tty.cursor_save() -- ... 执行其他操作 ... -- 恢复光标位置 tty.cursor_restore() ``` ## tty.cursor\_and\_attrs\_save * 保存当前光标位置和颜色属性 #### 函数原型 ::: tip API ```lua tty.cursor_and_attrs_save() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.cursor_and_attrs_save() ``` ## tty.cursor\_and\_attrs\_restore * 恢复保存的光标位置和颜色属性 #### 函数原型 ::: tip API ```lua tty.cursor_and_attrs_restore() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") -- 保存光标和属性 tty.cursor_and_attrs_save() -- ... 改变颜色,执行其他操作 ... -- 恢复光标位置和颜色 tty.cursor_and_attrs_restore() ``` ## tty.cr * 回车 #### 函数原型 ::: tip API ```lua tty.cr() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.cr() ``` ## tty.flush * 刷新终端输出 #### 函数原型 ::: tip API ```lua tty.flush() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | tty | 返回 tty 模块以支持链式调用 | #### 用法说明 ```lua import("core.base.tty") tty.flush() ``` ## tty.shell * 获取 shell 名称 #### 函数原型 ::: tip API ```lua tty.shell() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回 shell 名称(例如:"bash"、"zsh"、"pwsh"、"powershell"、"cmd") | #### 用法说明 ```lua import("core.base.tty") local shell = tty.shell() print("当前 shell:", shell) -- 输出: "bash"、"zsh" 等 ``` ## tty.term * 获取终端名称 #### 函数原型 ::: tip API ```lua tty.term() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回终端名称(例如:"xterm"、"vscode"、"windows-terminal"、"mintty") | #### 用法说明 ```lua import("core.base.tty") local term = tty.term() print("终端:", term) -- 输出: "xterm"、"vscode" 等 ``` ## tty.has\_emoji * 检查终端是否支持表情符号 #### 函数原型 ::: tip API ```lua tty.has_emoji() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持表情符号返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.tty") if tty.has_emoji() then print("支持表情符号!") else print("不支持表情符号") end ``` ## tty.has\_vtansi * 检查终端是否支持 VT-ANSI 序列 #### 函数原型 ::: tip API ```lua tty.has_vtansi() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持 VT-ANSI 返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.tty") if tty.has_vtansi() then -- 使用高级终端功能 tty.erase_screen() end ``` ## tty.has\_color8 * 检查终端是否支持 8 色 #### 函数原型 ::: tip API ```lua tty.has_color8() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持 8 色返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.tty") if tty.has_color8() then -- 使用 8 色转义序列 end ``` ## tty.has\_color256 * 检查终端是否支持 256 色 #### 函数原型 ::: tip API ```lua tty.has_color256() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持 256 色返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.tty") if tty.has_color256() then -- 使用 256 色转义序列 end ``` ## tty.has\_color24 * 检查终端是否支持 24 位真彩色 #### 函数原型 ::: tip API ```lua tty.has_color24() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持 24 位颜色返回 true,否则返回 false | #### 用法说明 ```lua import("core.base.tty") if tty.has_color24() then -- 使用 24 位真彩色转义序列 end ``` ## tty.term\_mode * 获取或设置终端模式 #### 函数原型 ::: tip API ```lua tty.term_mode(stdtype: , newmode?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | stdtype | 必需。标准流类型("stdin"、"stdout"、"stderr") | | newmode | 可选。要设置的新终端模式 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回旧的终端模式 | #### 用法说明 ```lua import("core.base.tty") -- 获取当前模式 local oldmode = tty.term_mode("stdout") -- 设置新模式 tty.term_mode("stdout", newmode) ``` --- --- url: /api/scripts/extension-modules/core/cache/detectcache.md --- # core.cache.detectcache This module provides detection result cache functionality for caching compiler/linker detection results. ::: tip TIP To use this module, you need to import it first: `import("core.cache.detectcache")` ::: ## detectcache.set * Set a cache value with single key #### Function Prototype ::: tip API ```lua detectcache.set(cachename: , key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.detectcache") -- Store a value with single key detectcache.set("mycache", "key1", {1, 2, 3}) ``` ## detectcache.get * Get a cache value with single key #### Function Prototype ::: tip API ```lua detectcache.get(cachename: , key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.detectcache") -- Get a value with single key local value = detectcache.get("mycache", "key1") ``` ## detectcache.set2 * Set a cache value with two-level keys #### Function Prototype ::: tip API ```lua detectcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.detectcache") -- Store a value with two-level keys detectcache.set2("mycache", "user", "name", "tboox") ``` ## detectcache.get2 * Get a cache value with two-level keys #### Function Prototype ::: tip API ```lua detectcache.get2(cachename: , key1: , key2: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.detectcache") -- Get a value with two-level keys local name = detectcache.get2("mycache", "user", "name") ``` ## detectcache.clear * Clear cache entries #### Function Prototype ::: tip API ```lua detectcache.clear(cachename?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Optional. Cache name string. If omitted, clears all caches | #### Return Value No return value #### Usage ```lua import("core.cache.detectcache") -- Clear a specific cache detectcache.clear("mycache") -- Clear all caches detectcache.clear() ``` --- --- url: /zh/api/scripts/extension-modules/core/cache/detectcache.md --- # core.cache.detectcache 此模块提供检测结果缓存功能,用于缓存编译器/链接器检测结果。 ::: tip 提示 使用此模块需要先导入:`import("core.cache.detectcache")` ::: ## detectcache.set * 设置缓存值(单键) #### 函数原型 ::: tip API ```lua detectcache.set(cachename: , key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.detectcache") -- 使用单个键存储值 detectcache.set("mycache", "key1", {1, 2, 3}) ``` ## detectcache.get * 获取缓存值(单键) #### 函数原型 ::: tip API ```lua detectcache.get(cachename: , key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.detectcache") -- 使用单个键获取值 local value = detectcache.get("mycache", "key1") ``` ## detectcache.set2 * 设置缓存值(二级键) #### 函数原型 ::: tip API ```lua detectcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.detectcache") -- 使用二级键存储值 detectcache.set2("mycache", "user", "name", "tboox") ``` ## detectcache.get2 * 获取缓存值(二级键) #### 函数原型 ::: tip API ```lua detectcache.get2(cachename: , key1: , key2: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.detectcache") -- 使用二级键获取值 local name = detectcache.get2("mycache", "user", "name") ``` ## detectcache.clear * 清除缓存条目 #### 函数原型 ::: tip API ```lua detectcache.clear(cachename?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 可选。缓存名称字符串。如果省略,清除所有缓存 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.detectcache") -- 清除指定缓存 detectcache.clear("mycache") -- 清除所有缓存 detectcache.clear() ``` --- --- url: /api/scripts/extension-modules/core/cache/global_detectcache.md --- # core.cache.global\_detectcache This module provides global detection result cache functionality for caching compiler/linker detection results across different build sessions. ::: tip TIP To use this module, you need to import it first: `import("core.cache.global_detectcache")` ::: ## global\_detectcache.set * Set a cache value with single key #### Function Prototype ::: tip API ```lua global_detectcache.set(cachename: , key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.global_detectcache") -- Store a value with single key global_detectcache.set("mycache", "key1", {1, 2, 3}) ``` ## global\_detectcache.get * Get a cache value with single key #### Function Prototype ::: tip API ```lua global_detectcache.get(cachename: , key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.global_detectcache") -- Get a value with single key local value = global_detectcache.get("mycache", "key1") ``` ## global\_detectcache.set2 * Set a cache value with two-level keys #### Function Prototype ::: tip API ```lua global_detectcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.global_detectcache") -- Store a value with two-level keys global_detectcache.set2("mycache", "user", "name", "tboox") ``` ## global\_detectcache.get2 * Get a cache value with two-level keys #### Function Prototype ::: tip API ```lua global_detectcache.get2(cachename: , key1: , key2: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.global_detectcache") -- Get a value with two-level keys local name = global_detectcache.get2("mycache", "user", "name") ``` ## global\_detectcache.clear * Clear cache entries #### Function Prototype ::: tip API ```lua global_detectcache.clear(cachename?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Optional. Cache name string. If omitted, clears all caches | #### Return Value No return value #### Usage ```lua import("core.cache.global_detectcache") -- Clear a specific cache global_detectcache.clear("mycache") -- Clear all caches global_detectcache.clear() ``` --- --- url: /zh/api/scripts/extension-modules/core/cache/global_detectcache.md --- # core.cache.global\_detectcache 此模块提供全局检测结果缓存功能,用于在不同构建会话之间缓存编译器/链接器检测结果。 ::: tip 提示 使用此模块需要先导入:`import("core.cache.global_detectcache")` ::: ## global\_detectcache.set * 设置缓存值(单键) #### 函数原型 ::: tip API ```lua global_detectcache.set(cachename: , key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.global_detectcache") -- 使用单个键存储值 global_detectcache.set("mycache", "key1", {1, 2, 3}) ``` ## global\_detectcache.get * 获取缓存值(单键) #### 函数原型 ::: tip API ```lua global_detectcache.get(cachename: , key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.global_detectcache") -- 使用单个键获取值 local value = global_detectcache.get("mycache", "key1") ``` ## global\_detectcache.set2 * 设置缓存值(二级键) #### 函数原型 ::: tip API ```lua global_detectcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.global_detectcache") -- 使用二级键存储值 global_detectcache.set2("mycache", "user", "name", "tboox") ``` ## global\_detectcache.get2 * 获取缓存值(二级键) #### 函数原型 ::: tip API ```lua global_detectcache.get2(cachename: , key1: , key2: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.global_detectcache") -- 使用二级键获取值 local name = global_detectcache.get2("mycache", "user", "name") ``` ## global\_detectcache.clear * 清除缓存条目 #### 函数原型 ::: tip API ```lua global_detectcache.clear(cachename?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 可选。缓存名称字符串。如果省略,清除所有缓存 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.global_detectcache") -- 清除指定缓存 global_detectcache.clear("mycache") -- 清除所有缓存 global_detectcache.clear() ``` --- --- url: /api/scripts/extension-modules/core/cache/globalcache.md --- # core.cache.globalcache This module provides global file-based cache functionality for persistent data storage across different build sessions. ::: tip TIP To use this module, you need to import it first: `import("core.cache.globalcache")` ::: ## globalcache.set * Set a cache value with single key #### Function Prototype ::: tip API ```lua globalcache.set(cachename: , key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.globalcache") -- Store a value with single key globalcache.set("mycache", "key1", {1, 2, 3}) ``` ## globalcache.get * Get a cache value with single key #### Function Prototype ::: tip API ```lua globalcache.get(cachename: , key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.globalcache") -- Get a value with single key local value = globalcache.get("mycache", "key1") ``` ## globalcache.set2 * Set a cache value with two-level keys #### Function Prototype ::: tip API ```lua globalcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.globalcache") -- Store a value with two-level keys globalcache.set2("mycache", "user", "name", "tboox") ``` ## globalcache.get2 * Get a cache value with two-level keys #### Function Prototype ::: tip API ```lua globalcache.get2(cachename: , key1: , key2: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.globalcache") -- Get a value with two-level keys local name = globalcache.get2("mycache", "user", "name") ``` ## globalcache.clear * Clear cache entries #### Function Prototype ::: tip API ```lua globalcache.clear(cachename?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Optional. Cache name string. If omitted, clears all caches | #### Return Value No return value #### Usage ```lua import("core.cache.globalcache") -- Clear a specific cache globalcache.clear("mycache") -- Clear all caches globalcache.clear() ``` --- --- url: /zh/api/scripts/extension-modules/core/cache/globalcache.md --- # core.cache.globalcache 此模块提供全局文件缓存功能,用于在不同构建会话之间持久化数据存储。 ::: tip 提示 使用此模块需要先导入:`import("core.cache.globalcache")` ::: ## globalcache.set * 设置缓存值(单键) #### 函数原型 ::: tip API ```lua globalcache.set(cachename: , key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.globalcache") -- 使用单个键存储值 globalcache.set("mycache", "key1", {1, 2, 3}) ``` ## globalcache.get * 获取缓存值(单键) #### 函数原型 ::: tip API ```lua globalcache.get(cachename: , key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.globalcache") -- 使用单个键获取值 local value = globalcache.get("mycache", "key1") ``` ## globalcache.set2 * 设置缓存值(二级键) #### 函数原型 ::: tip API ```lua globalcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.globalcache") -- 使用二级键存储值 globalcache.set2("mycache", "user", "name", "tboox") ``` ## globalcache.get2 * 获取缓存值(二级键) #### 函数原型 ::: tip API ```lua globalcache.get2(cachename: , key1: , key2: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.globalcache") -- 使用二级键获取值 local name = globalcache.get2("mycache", "user", "name") ``` ## globalcache.clear * 清除缓存条目 #### 函数原型 ::: tip API ```lua globalcache.clear(cachename?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 可选。缓存名称字符串。如果省略,清除所有缓存 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.globalcache") -- 清除指定缓存 globalcache.clear("mycache") -- 清除所有缓存 globalcache.clear() ``` --- --- url: /api/scripts/extension-modules/core/cache/localcache.md --- # core.cache.localcache This module provides local file-based cache functionality for persistent data storage during build operations. ::: tip TIP To use this module, you need to import it first: `import("core.cache.localcache")` ::: ## localcache.set * Set a cache value with single key #### Function Prototype ::: tip API ```lua localcache.set(cachename: , key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.localcache") -- Store a value with single key localcache.set("mycache", "key1", {1, 2, 3}) ``` ## localcache.get * Get a cache value with single key #### Function Prototype ::: tip API ```lua localcache.get(cachename: , key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.localcache") -- Get a value with single key local value = localcache.get("mycache", "key1") ``` ## localcache.set2 * Set a cache value with two-level keys #### Function Prototype ::: tip API ```lua localcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.localcache") -- Store a value with two-level keys localcache.set2("mycache", "user", "name", "tboox") ``` ## localcache.get2 * Get a cache value with two-level keys #### Function Prototype ::: tip API ```lua localcache.get2(cachename: , key1: , key2: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.localcache") -- Get a value with two-level keys local name = localcache.get2("mycache", "user", "name") ``` ## localcache.clear * Clear cache entries #### Function Prototype ::: tip API ```lua localcache.clear(cachename?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Optional. Cache name string. If omitted, clears all caches | #### Return Value No return value #### Usage ```lua import("core.cache.localcache") -- Clear a specific cache localcache.clear("mycache") -- Clear all caches localcache.clear() ``` --- --- url: /zh/api/scripts/extension-modules/core/cache/localcache.md --- # core.cache.localcache 此模块提供本地文件缓存功能,用于构建操作期间的持久化数据存储。 ::: tip 提示 使用此模块需要先导入:`import("core.cache.localcache")` ::: ## localcache.set * 设置缓存值(单键) #### 函数原型 ::: tip API ```lua localcache.set(cachename: , key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.localcache") -- 使用单个键存储值 localcache.set("mycache", "key1", {1, 2, 3}) ``` ## localcache.get * 获取缓存值(单键) #### 函数原型 ::: tip API ```lua localcache.get(cachename: , key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.localcache") -- 使用单个键获取值 local value = localcache.get("mycache", "key1") ``` ## localcache.set2 * 设置缓存值(二级键) #### 函数原型 ::: tip API ```lua localcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.localcache") -- 使用二级键存储值 localcache.set2("mycache", "user", "name", "tboox") ``` ## localcache.get2 * 获取缓存值(二级键) #### 函数原型 ::: tip API ```lua localcache.get2(cachename: , key1: , key2: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.localcache") -- 使用二级键获取值 local name = localcache.get2("mycache", "user", "name") ``` ## localcache.clear * 清除缓存条目 #### 函数原型 ::: tip API ```lua localcache.clear(cachename?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 可选。缓存名称字符串。如果省略,清除所有缓存 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.localcache") -- 清除指定缓存 localcache.clear("mycache") -- 清除所有缓存 localcache.clear() ``` --- --- url: /api/scripts/extension-modules/core/cache/memcache.md --- # core.cache.memcache This module provides in-memory cache functionality for temporary data storage during build operations. ::: tip TIP To use this module, you need to import it first: `import("core.cache.memcache")` ::: ## memcache.set * Set a cache value with single key #### Function Prototype ::: tip API ```lua memcache.set(cachename: , key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.memcache") -- Store a value with single key memcache.set("mycache", "key1", {1, 2, 3}) ``` ## memcache.get * Get a cache value with single key #### Function Prototype ::: tip API ```lua memcache.get(cachename: , key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key | Required. Cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.memcache") -- Get a value with single key local value = memcache.get("mycache", "key1") ``` ## memcache.set2 * Set a cache value with two-level keys #### Function Prototype ::: tip API ```lua memcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | | value | Required. Cache value (can be any Lua value) | #### Return Value No return value #### Usage ```lua import("core.cache.memcache") -- Store a value with two-level keys memcache.set2("mycache", "user", "name", "tboox") ``` ## memcache.get2 * Get a cache value with two-level keys #### Function Prototype ::: tip API ```lua memcache.get2(cachename: , key1: , key2: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Required. Cache name string | | key1 | Required. First-level cache key string | | key2 | Required. Second-level cache key string | #### Return Value | Type | Description | |------|-------------| | any | Returns the cached value if found, nil otherwise | #### Usage ```lua import("core.cache.memcache") -- Get a value with two-level keys local name = memcache.get2("mycache", "user", "name") ``` ## memcache.clear * Clear cache entries #### Function Prototype ::: tip API ```lua memcache.clear(cachename?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cachename | Optional. Cache name string. If omitted, clears all caches | #### Return Value No return value #### Usage ```lua import("core.cache.memcache") -- Clear a specific cache memcache.clear("mycache") -- Clear all caches memcache.clear() ``` --- --- url: /zh/api/scripts/extension-modules/core/cache/memcache.md --- # core.cache.memcache 此模块提供内存缓存功能,用于构建操作期间的临时数据存储。 ::: tip 提示 使用此模块需要先导入:`import("core.cache.memcache")` ::: ## memcache.set * 设置缓存值(单键) #### 函数原型 ::: tip API ```lua memcache.set(cachename: , key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.memcache") -- 使用单个键存储值 memcache.set("mycache", "key1", {1, 2, 3}) ``` ## memcache.get * 获取缓存值(单键) #### 函数原型 ::: tip API ```lua memcache.get(cachename: , key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key | 必需。缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.memcache") -- 使用单个键获取值 local value = memcache.get("mycache", "key1") ``` ## memcache.set2 * 设置缓存值(二级键) #### 函数原型 ::: tip API ```lua memcache.set2(cachename: , key1: , key2: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | | value | 必需。缓存值(可以是任意 Lua 值) | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.memcache") -- 使用二级键存储值 memcache.set2("mycache", "user", "name", "tboox") ``` ## memcache.get2 * 获取缓存值(二级键) #### 函数原型 ::: tip API ```lua memcache.get2(cachename: , key1: , key2: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 必需。缓存名称字符串 | | key1 | 必需。一级缓存键字符串 | | key2 | 必需。二级缓存键字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 找到返回缓存值,未找到返回 nil | #### 用法说明 ```lua import("core.cache.memcache") -- 使用二级键获取值 local name = memcache.get2("mycache", "user", "name") ``` ## memcache.clear * 清除缓存条目 #### 函数原型 ::: tip API ```lua memcache.clear(cachename?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cachename | 可选。缓存名称字符串。如果省略,清除所有缓存 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("core.cache.memcache") -- 清除指定缓存 memcache.clear("mycache") -- 清除所有缓存 memcache.clear() ``` --- --- url: /api/scripts/extension-modules/core/language/language.md --- # core.language.language Used to obtain information about the compiled language, generally used for the operation of code files. ## language.extensions * Get a list of code suffixes for all languages #### Function Prototype ::: tip API ```lua language.extensions() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | table | Returns a table with file extensions as keys and language source types as values | #### Usage 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 #### Function Prototype ::: tip API ```lua language.targetkinds() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | table | Returns a table with target types as keys and lists of supported linkers as values | #### Usage Get a list of target types in all languages. The results are as follows: ```lua { binary = {"ld", "gcld", "dcld"}, static = {"ar", "gcar", "dcar"}, shared = {"sh", "dcsh"} } ``` ## language.sourcekinds * Get a list of source file types in all languages #### Function Prototype ::: tip API ```lua language.sourcekinds() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | table | Returns a table with source file types as keys and file extensions or extension lists as values | #### Usage 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 #### Function Prototype ::: tip API ```lua language.sourceflags() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | table | Returns a table with source file types as keys and compilation option name lists as values | #### Usage 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 #### Function Prototype ::: tip API ```lua language.load(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Language name, e.g., "c++", "c" | #### Return Value | Type | Description | |------|-------------| | language | Returns a language object, or nil if it doesn't exist | #### Usage 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 #### Function Prototype ::: tip API ```lua language.load_sk(sourcekind: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcekind | Required. Source file type, e.g., "cc", "cxx", "mm" | #### Return Value | Type | Description | |------|-------------| | language | Returns a language object, or nil if it doesn't exist | #### Usage 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 #### Function Prototype ::: tip API ```lua language.load_ex(extension: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | extension | Required. Source file extension, e.g., ".cpp", ".c" | #### Return Value | Type | Description | |------|-------------| | language | Returns a language object, or nil if it doesn't exist | #### Usage Load specific language objects from the source file extension: `.cc, .c, .cpp, .mm, .swift, .go ..`, for example: ```lua local lang = language.load_ex(".cpp") if lang then print(lang:name()) end ``` ## language.sourcekind\_of * Get the source file type of the specified source file #### Function Prototype ::: tip API ```lua language.sourcekind_of(filepath: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filepath | Required. Source file path | #### Return Value | Type | Description | |------|-------------| | string | Returns the source file type, e.g., "cxx", "cc" | #### Usage 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) --- --- url: /zh/api/scripts/extension-modules/core/language/language.md --- # core.language.language 用于获取编译语言相关信息,一般用于代码文件的操作。 ## language.extensions * 获取所有语言的代码后缀名列表 #### 函数原型 ::: tip API ```lua language.extensions() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回一个表,键为文件后缀名,值为语言源类型 | #### 用法说明 获取所有语言的代码后缀名列表。 获取结果如下: ```lua { [".c"] = cc, [".cc"] = cxx, [".cpp"] = cxx, [".m"] = mm, [".mm"] = mxx, [".swift"] = sc, [".go"] = gc } ``` ## language.targetkinds * 获取所有语言的目标类型列表 #### 函数原型 ::: tip API ```lua language.targetkinds() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回一个表,键为目标类型,值为该类型支持的链接器列表 | #### 用法说明 获取所有语言的目标类型列表。 获取结果如下: ```lua { binary = {"ld", "gcld", "dcld"}, static = {"ar", "gcar", "dcar"}, shared = {"sh", "dcsh"} } ``` ## language.sourcekinds * 获取所有语言的源文件类型列表 #### 函数原型 ::: tip API ```lua language.sourcekinds() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回一个表,键为源文件类型,值为文件后缀名或后缀名列表 | #### 用法说明 获取所有语言的源文件类型列表。 获取结果如下: ```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 * 加载所有语言的源文件编译选项名列表 #### 函数原型 ::: tip API ```lua language.sourceflags() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回一个表,键为源文件类型,值为编译选项名列表 | #### 用法说明 加载所有语言的源文件编译选项名列表。 获取结果如下: ```lua { cc = {"cflags", "cxflags"}, cxx = {"cxxflags", "cxflags"}, ... } ``` ## language.load * 加载指定语言 #### 函数原型 ::: tip API ```lua language.load(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。语言名称,例如 "c++", "c" | #### 返回值说明 | 类型 | 描述 | |------|------| | language | 返回语言对象,如果不存在则返回 nil | #### 用法说明 从语言名称加载具体语言对象,例如: ```lua local lang = language.load("c++") if lang then print(lang:name()) end ``` ## language.load\_sk * 从源文件类型加载指定语言 #### 函数原型 ::: tip API ```lua language.load_sk(sourcekind: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcekind | 必需。源文件类型,例如 "cc", "cxx", "mm" | #### 返回值说明 | 类型 | 描述 | |------|------| | language | 返回语言对象,如果不存在则返回 nil | #### 用法说明 从源文件类型:`cc, cxx, mm, mxx, sc, gc, as ..`加载具体语言对象,例如: ```lua local lang = language.load_sk("cxx") if lang then print(lang:name()) end ``` ## language.load\_ex * 从源文件后缀名加载指定语言 #### 函数原型 ::: tip API ```lua language.load_ex(extension: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | extension | 必需。源文件后缀名,例如 ".cpp", ".c" | #### 返回值说明 | 类型 | 描述 | |------|------| | language | 返回语言对象,如果不存在则返回 nil | #### 用法说明 从源文件后缀名:`.cc, .c, .cpp, .mm, .swift, .go ..`加载具体语言对象,例如: ```lua local lang = language.load_ex(".cpp") if lang then print(lang:name()) end ``` ## language.sourcekind\_of * 获取指定源文件的源文件类型 #### 函数原型 ::: tip API ```lua language.sourcekind_of(filepath: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 必需。源文件路径 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回源文件类型,例如 "cxx", "cc" | #### 用法说明 也就是从给定的一个源文件路径,获取它是属于那种源文件类型,例如: ```lua print(language.sourcekind_of("/xxxx/test.cpp")) ``` 显示结果为:`cxx`,也就是`c++`类型,具体对应列表见:[language.sourcekinds](#language-sourcekinds) --- --- url: /api/scripts/extension-modules/core/project/config.md --- # 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`. ## config.get * Get the specified configuration value #### Function Prototype ::: tip API ```lua config.get(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Configuration item name | #### Return Value | Type | Description | |------|-------------| | any | Returns the configuration value, or nil if it doesn't exist | #### Usage 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 #### Function Prototype ::: tip API ```lua config.load() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value This function has no return value. #### Usage 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 #### Function Prototype ::: tip API ```lua config.arch() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Returns the architecture name, e.g., "x86\_64", "armv7" | #### Usage 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 #### Function Prototype ::: tip API ```lua config.plat() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Returns the platform name, e.g., "macosx", "linux" | #### Usage 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 #### Function Prototype ::: tip API ```lua config.mode() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Returns the compilation mode, e.g., "debug", "release" | #### Usage That is to get the platform configuration of `xmake f|config --mode=debug`, which is equivalent to `config.get("mode")`. ## config.builddir * Get the output directory configuration of the current project #### Function Prototype ::: tip API ```lua config.builddir() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Returns the build output directory path | #### Usage That is to get the platform configuration of `xmake f|config -o /tmp/output`, which is equivalent to `config.get("builddir")`. ## config.directory * Get the configuration information directory of the current project #### Function Prototype ::: tip API ```lua config.directory() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Returns the configuration information directory path | #### Usage Get the storage directory of the project configuration, the default is: `projectdir/.config` ## config.dump * Print out all configuration information of the current project #### Function Prototype ::: tip API ```lua config.dump() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | table | Returns a table containing all configuration information | #### Usage 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", builddir = "build", mxx = "xcrun -sdk macosx clang++", ld = "xcrun -sdk macosx clang++", mode = "release", kind = "static" } ``` --- --- url: /zh/api/scripts/extension-modules/core/project/config.md --- # core.project.config 用于获取工程编译时候的配置信息,也就是`xmake f|config --xxx=val` 传入的参数选项值。 ## config.get * 获取指定配置值 #### 函数原型 ::: tip API ```lua config.get(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。配置项名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 返回配置值,如果不存在则返回 nil | #### 用法说明 用于获取`xmake f|config --xxx=val`的配置值,例如: ```lua target("test") on_run(function (target) -- 导入配置模块 import("core.project.config") -- 获取配置值 print(config.get("xxx")) end) ``` ## config.load * 加载配置 #### 函数原型 ::: tip API ```lua config.load() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 此函数无返回值。 #### 用法说明 一般用于插件开发中,插件任务中不像工程的自定义脚本,环境需要自己初始化加载,默认工程配置是没有被加载的,如果要用[config.get](#config-get)接口获取工程配置,那么需要先: ```lua -- 导入配置模块 import("core.project.config") function main(...) -- 先加载工程配置 config.load() -- 获取配置值 print(config.get("xxx")) end ``` ## config.arch * 获取当前工程的架构配置 #### 函数原型 ::: tip API ```lua config.arch() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回架构名称,例如 "x86\_64", "armv7" | #### 用法说明 也就是获取`xmake f|config --arch=armv7`的平台配置,相当于`config.get("arch")`。 ## config.plat * 获取当前工程的平台配置 #### 函数原型 ::: tip API ```lua config.plat() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回平台名称,例如 "macosx", "linux" | #### 用法说明 也就是获取`xmake f|config --plat=iphoneos`的平台配置,相当于`config.get("plat")`。 ## config.mode * 获取当前工程的编译模式配置 #### 函数原型 ::: tip API ```lua config.mode() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回编译模式,例如 "debug", "release" | #### 用法说明 也就是获取`xmake f|config --mode=debug`的平台配置,相当于`config.get("mode")`。 ## config.builddir * 获取当前工程的输出目录配置 #### 函数原型 ::: tip API ```lua config.builddir() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回构建输出目录路径 | #### 用法说明 也就是获取`xmake f|config -o /tmp/output`的平台配置,相当于`config.get("builddir")`。 ## config.directory * 获取当前工程的配置信息目录 #### 函数原型 ::: tip API ```lua config.directory() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回配置信息目录路径 | #### 用法说明 获取工程配置的存储目录,默认为:`projectdir/.config` ## config.dump * 打印输出当前工程的所有配置信息 #### 函数原型 ::: tip API ```lua config.dump() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回包含所有配置信息的表 | #### 用法说明 打印输出当前工程的所有配置信息,输出结果例如: ```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", builddir = "build", mxx = "xcrun -sdk macosx clang++", ld = "xcrun -sdk macosx clang++", mode = "release", kind = "static" } ``` --- --- url: /api/scripts/extension-modules/core/project/project.md --- # 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](/api/description/project-target), [option](/api/description/configuration-option), etc. ## project.target * Get the specified project target object #### Function Prototype ::: tip API ```lua project.target(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Target name | #### Return Value | Type | Description | |------|-------------| | target | Returns the target object, or nil if it doesn't exist | #### Usage Get and access the specified project target configuration, for example: ```lua local target = project.target("test") if target then -- Get the target name print(target:name()) -- Get the target directory, available after version 2.1.9 print(target:targetdir()) -- Get the target file name print(target:targetfile()) -- Get the target type, which is: binary, static, shared print(target:targetkind()) -- 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 #### Function Prototype ::: tip API ```lua project.targets() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | table | Returns a table containing all target objects, with target names as keys and target objects as values | #### Usage Returns all compilation targets for the current project, for example: ```lua for targetname, target in pairs(project.targets()) do print(target:targetfile()) end ``` ## project.option * Get the specified option object #### Function Prototype ::: tip API ```lua project.option(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Option name | #### Return Value | Type | Description | |------|-------------| | option | Returns the option object, or nil if it doesn't exist | #### Usage 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 #### Function Prototype ::: tip API ```lua project.options() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | table | Returns a table containing all option objects, with option names as keys and option objects as values | #### Usage Returns all compilation targets for the current project, for example: ```lua for optionname, option in pairs(project.options()) do print(option:enabled()) end ``` ## project.name * Get the current project name #### Function Prototype ::: tip API ```lua project.name() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Returns the project name | #### Usage That is, get the project name configuration of [set\_project](/api/description/global-interfaces#set-project). ```lua print(project.name()) ``` ## project.version * Get the current project version number #### Function Prototype ::: tip API ```lua project.version() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Returns the project version number | #### Usage That is, get [set\_version](/api/description/global-interfaces#set-version) project version configuration. ```lua print(project.version()) ``` --- --- url: /zh/api/scripts/extension-modules/core/project/project.md --- # core.project.project 用于获取当前工程的一些描述信息,也就是在`xmake.lua`工程描述文件中定义的配置信息,例如:[target](/zh/api/description/project-target)、 [option](/zh/api/description/configuration-option) 等。 ## project.target * 获取指定工程目标对象 #### 函数原型 ::: tip API ```lua project.target(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。目标名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | target | 返回目标对象,如果不存在则返回 nil | #### 用法说明 获取和访问指定工程目标配置,例如: ```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 * 获取工程目标对象列表 #### 函数原型 ::: tip API ```lua project.targets() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回一个表,包含所有目标对象,键为目标名称,值为目标对象 | #### 用法说明 返回当前工程的所有编译目标,例如: ```lua for targetname, target in pairs(project.targets()) do print(target:targetfile()) end ``` ## project.option * 获取指定选项对象 #### 函数原型 ::: tip API ```lua project.option(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。选项名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | option | 返回选项对象,如果不存在则返回 nil | #### 用法说明 获取和访问工程中指定的选项对象,例如: ```lua local option = project.option("test") if option:enabled() then option:enable(false) end ``` ## project.options * 获取工程所有选项对象 #### 函数原型 ::: tip API ```lua project.options() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回一个表,包含所有选项对象,键为选项名称,值为选项对象 | #### 用法说明 返回当前工程的所有编译目标,例如: ```lua for optionname, option in pairs(project.options()) do print(option:enabled()) end ``` ## project.name * 获取当前工程名 #### 函数原型 ::: tip API ```lua project.name() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回工程名称 | #### 用法说明 也就是获取[set\_project](/zh/api/description/global-interfaces#set-project)的工程名配置。 ```lua print(project.name()) ``` ## project.version * 获取当前工程版本号 #### 函数原型 ::: tip API ```lua project.version() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回工程版本号 | #### 用法说明 也就是获取[set\_version](/zh/api/description/global-interfaces#set-version)的工程版本配置。 ```lua print(project.version()) ``` --- --- url: /api/scripts/extension-modules/core/tool/compiler.md --- # core.tool.compiler Compiler related operations, often used for plugin development. ## compiler.compile * Perform compilation #### Function Prototype ::: tip API ```lua compiler.compile(sourcefile: , objectfile: , depfile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcefile | Required. Source file path | | objectfile | Required. Target file path | | depfile | Optional. Dependency file path | | opt | Optional. Option parameters, supports `target` | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage 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](/api/description/project-target) is the project target, here is the specific compile option that is mainly used to get the target. For the project target object, see: [core.project.project](/api/scripts/extension-modules/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 #### Function Prototype ::: tip API ```lua compiler.compcmd(sourcefile: , objectfile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcefile | Required. Source file path | | objectfile | Required. Target file path | | opt | Optional. Option parameters, supports `target` and `configs` | #### Return Value | Type | Description | |------|-------------| | string | Returns compile command string | #### Usage 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", {configs = {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 #### Function Prototype ::: tip API ```lua compiler.compargv(sourcefile: , objectfile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcefile | Required. Source file path | | objectfile | Required. Target file path | | opt | Optional. Option parameters | #### Return Values | Type | Description | |------|-------------| | string | Compiler program path | | table | Compile arguments list | #### Usage A little different from [compiler.compcmd](#compiler-compcmd) 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 #### Function Prototype ::: tip API ```lua compiler.compflags(sourcefile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcefile | Required. Source file path | | opt | Optional. Option parameters, supports `target` | #### Return Value | Type | Description | |------|-------------| | table | Returns compilation options list array | #### Usage Get the compile option string part of [compiler.compcmd](#compiler-compcmd) without shellname 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 #### Function Prototype ::: tip API ```lua compiler.has_flags(sourcekind: , flag: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcekind | Required. Source file type, e.g., "c", "cxx" | | flag | Required. Compilation option to check | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if supported, false otherwise | #### Usage Although it can be judged by [lib.detect.has\_flags](/api/scripts/extension-modules/lib/detect#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 #### Function Prototype ::: tip API ```lua compiler.features(sourcekind: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcekind | Required. Source file type, e.g., "c", "cxx" | | opt | Optional. Option parameters, supports `target` and `configs` | #### Return Value | Type | Description | |------|-------------| | table | Returns feature list array | #### Usage Although it can be obtained by [lib.detect.features](/api/scripts/extension-modules/lib/detect#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", {configs = {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, configs = {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 #### Function Prototype ::: tip API ```lua compiler.has_features(features: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | features | Required. Feature name or feature name list | | opt | Optional. Option parameters, supports `languages`, `target`, `configs` | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if supported, false otherwise | #### Usage Although it can be obtained by [lib.detect.has\_features](/api/scripts/extension-modules/lib/detect#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). --- --- url: /zh/api/scripts/extension-modules/core/tool/compiler.md --- # core.tool.compiler 编译器相关操作,常用于插件开发。 ## compiler.compile * 执行编译 #### 函数原型 ::: tip API ```lua compiler.compile(sourcefile: , objectfile: , depfile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcefile | 必需。源文件路径 | | objectfile | 必需。目标文件路径 | | depfile | 可选。依赖文件路径 | | opt | 可选。选项参数,支持 `target` | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 编译成功返回 true,失败返回 false | #### 用法说明 针对target,链接指定对象文件列表,生成对应的目标文件,例如: ```lua compiler.compile("xxx.c", "xxx.o", "xxx.h.d", {target = target}) ``` 其中[target](/zh/api/description/project-target),为工程目标,这里传入,主要用于获取target特定的编译选项, 具体如果获取工程目标对象,见:[core.project.project](/zh/api/scripts/extension-modules/core/project/project)。 而`xxx.h.d`文件用于存储为此源文件的头文件依赖文件列表,最后这两个参数都是可选的,编译的时候可以不传他们: ```lua compiler.compile("xxx.c", "xxx.o") ``` 来单纯编译一个源文件。 ## compiler.compcmd * 获取编译命令行 #### 函数原型 ::: tip API ```lua compiler.compcmd(sourcefile: , objectfile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcefile | 必需。源文件路径 | | objectfile | 必需。目标文件路径 | | opt | 可选。选项参数,支持 `target` 和 `configs` | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回编译命令字符串 | #### 用法说明 直接获取[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", {configs = {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 * 获取编译命令行列表 #### 函数原型 ::: tip API ```lua compiler.compargv(sourcefile: , objectfile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcefile | 必需。源文件路径 | | objectfile | 必需。目标文件路径 | | opt | 可选。选项参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 编译器程序路径 | | table | 编译参数列表 | #### 用法说明 跟[compiler.compcmd](#compiler-compcmd)稍微有点区别的是,此接口返回的是参数列表,table表示,更加方便操作: ```lua local program, argv = compiler.compargv("xxx.c", "xxx.o") ``` ## compiler.compflags * 获取编译选项 #### 函数原型 ::: tip API ```lua compiler.compflags(sourcefile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcefile | 必需。源文件路径 | | opt | 可选。选项参数,支持 `target` | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回编译选项列表数组 | #### 用法说明 获取[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 * 判断指定编译选项是否支持 #### 函数原型 ::: tip API ```lua compiler.has_flags(sourcekind: , flag: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcekind | 必需。源文件类型,例如 "c", "cxx" | | flag | 必需。要判断的编译选项 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持返回 true,不支持返回 false | #### 用法说明 虽然通过[lib.detect.has\_flags](/zh/api/scripts/extension-modules/lib/detect#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 * 获取所有编译器特性 #### 函数原型 ::: tip API ```lua compiler.features(sourcekind: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcekind | 必需。源文件类型,例如 "c", "cxx" | | opt | 可选。选项参数,支持 `target` 和 `configs` | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回特性列表数组 | #### 用法说明 虽然通过[lib.detect.features](/zh/api/scripts/extension-modules/lib/detect#detect-features)也能获取,但是那个接口更加底层,需要指定编译器名称。 而此接口只需要指定语言类型,它会自动切换选择当前支持的编译器,然后获取当前的编译器特性列表。 ```lua -- 获取当前c语言编译器的所有特性 local features = compiler.features("c") -- 获取当前c++语言编译器的所有特性,启用c++11标准,否则获取不到新标准的特性 local features = compiler.features("cxx", {configs = {cxxflags = "-std=c++11"}}) -- 获取当前c++语言编译器的所有特性,传递工程target的所有配置信息 local features = compiler.features("cxx", {target = target, configs = {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 * 判断指定的编译器特性是否支持 #### 函数原型 ::: tip API ```lua compiler.has_features(features: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | features | 必需。特性名称或特性名称列表 | | opt | 可选。选项参数,支持 `languages`, `target`, `configs` 等 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持返回 true,不支持返回 false | #### 用法说明 虽然通过[lib.detect.has\_features](/zh/api/scripts/extension-modules/lib/detect#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)。 --- --- url: /api/scripts/extension-modules/core/tool/linker.md --- # core.tool.linker Linker related operations, often used for plugin development. ## linker.link * Execute link #### Function Prototype ::: tip API ```lua linker.link(targetkind: , sourcekinds: , objectfiles:
, targetfile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | targetkind | Required. Target type, supports "binary", "static", "shared" | | sourcekinds | Required. Source file type or type list, e.g., "cc", "cxx", {"cc", "mxx", "sc"} | | objectfiles | Required. Object file path list | | targetfile | Required. Target file path | | opt | Optional. Option parameters, supports `target` | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage 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](/api/description/project-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](/api/scripts/extension-modules/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 #### Function Prototype ::: tip API ```lua linker.linkcmd(targetkind: , sourcekinds: , objectfiles:
, targetfile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | targetkind | Required. Target type, supports "binary", "static", "shared" | | sourcekinds | Required. Source file type or type list | | objectfiles | Required. Object file path list | | targetfile | Required. Target file path | | opt | Optional. Option parameters, supports `target` and `configs` | #### Return Value | Type | Description | |------|-------------| | string | Returns link command string | #### Usage 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(), {configs = {linkdirs = "/usr/lib"}}) ``` ## linker.linkargv * Get a list of link command line arguments #### Function Prototype ::: tip API ```lua linker.linkargv(targetkind: , sourcekinds: , objectfiles:
, targetfile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | targetkind | Required. Target type, supports "binary", "static", "shared" | | sourcekinds | Required. Source file type or type list | | objectfiles | Required. Object file path list | | targetfile | Required. Target file path | | opt | Optional. Option parameters | #### Return Values | Type | Description | |------|-------------| | string | Linker program path | | table | Link arguments list | #### Usage 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](/api/scripts/builtin-modules/os#os-runv): `os.runv(linker.linkargv(..))` ## linker.linkflags * Get link options #### Function Prototype ::: tip API ```lua linker.linkflags(targetkind: , sourcekinds: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | targetkind | Required. Target type, supports "binary", "static", "shared" | | sourcekinds | Required. Source file type or type list | | opt | Optional. Option parameters, supports `target` | #### Return Value | Type | Description | |------|-------------| | table | Returns link options list array | #### Usage 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 #### Function Prototype ::: tip API ```lua linker.has_flags(targetkind: , sourcekinds: , flags: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | targetkind | Required. Target type, e.g., "binary", "static" | | sourcekinds | Required. Source file type or type list | | flags | Required. Link option to check | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if supported, false otherwise | #### Usage Although it can be judged by [lib.detect.has\_flags](/api/scripts/extension-modules/lib/detect#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 ``` --- --- url: /zh/api/scripts/extension-modules/core/tool/linker.md --- # core.tool.linker 链接器相关操作,常用于插件开发。 ## linker.link * 执行链接 #### 函数原型 ::: tip API ```lua linker.link(targetkind: , sourcekinds: , objectfiles:
, targetfile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | targetkind | 必需。目标类型,支持 "binary", "static", "shared" | | sourcekinds | 必需。源文件类型或类型列表,例如 "cc", "cxx", {"cc", "mxx", "sc"} | | objectfiles | 必需。对象文件路径列表 | | targetfile | 必需。目标文件路径 | | opt | 可选。选项参数,支持 `target` | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 链接成功返回 true,失败返回 false | #### 用法说明 针对target,链接指定对象文件列表,生成对应的目标文件,例如: ```lua linker.link("binary", "cc", {"a.o", "b.o", "c.o"}, target:targetfile(), {target = target}) ``` 其中[target](/zh/api/description/project-target),为工程目标,这里传入,主要用于获取target特定的链接选项, 具体如果获取工程目标对象,见:[core.project.project](/zh/api/scripts/extension-modules/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 * 获取链接命令行字符串 #### 函数原型 ::: tip API ```lua linker.linkcmd(targetkind: , sourcekinds: , objectfiles:
, targetfile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | targetkind | 必需。目标类型,支持 "binary", "static", "shared" | | sourcekinds | 必需。源文件类型或类型列表 | | objectfiles | 必需。对象文件路径列表 | | targetfile | 必需。目标文件路径 | | opt | 可选。选项参数,支持 `target` 和 `configs` | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回链接命令字符串 | #### 用法说明 直接获取[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(), {configs = {linkdirs = "/usr/lib"}}) ``` ## linker.linkargv * 获取链接命令行参数列表 #### 函数原型 ::: tip API ```lua linker.linkargv(targetkind: , sourcekinds: , objectfiles:
, targetfile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | targetkind | 必需。目标类型,支持 "binary", "static", "shared" | | sourcekinds | 必需。源文件类型或类型列表 | | objectfiles | 必需。对象文件路径列表 | | targetfile | 必需。目标文件路径 | | opt | 可选。选项参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 链接器程序路径 | | table | 链接参数列表 | #### 用法说明 跟[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](/zh/api/scripts/builtin-modules/os#os-runv)来直接运行它:`os.runv(linker.linkargv(..))` ## linker.linkflags * 获取链接选项 #### 函数原型 ::: tip API ```lua linker.linkflags(targetkind: , sourcekinds: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | targetkind | 必需。目标类型,支持 "binary", "static", "shared" | | sourcekinds | 必需。源文件类型或类型列表 | | opt | 可选。选项参数,支持 `target` | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回链接选项列表数组 | #### 用法说明 获取[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 * 判断指定链接选项是否支持 #### 函数原型 ::: tip API ```lua linker.has_flags(targetkind: , sourcekinds: , flags: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | targetkind | 必需。目标类型,例如 "binary", "static" | | sourcekinds | 必需。源文件类型或类型列表 | | flags | 必需。要判断的链接选项 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持返回 true,不支持返回 false | #### 用法说明 虽然通过[lib.detect.has\_flags](/zh/api/scripts/extension-modules/lib/detect#detect-has_flags)也能判断,但是那个接口更加底层,需要指定链接器名称 而此接口只需要指定target的目标类型,源文件类型,它会自动切换选择当前支持的链接器。 ```lua if linker.has_flags(target:targetkind(), target:sourcekinds(), "-L/usr/lib -lpthread") then -- ok end ``` --- --- url: /api/scripts/extension-modules/core/ui/action.md --- # core.ui.action This module defines action enumeration constants for the UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.action")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `action` module provides type constants for UI event callback actions, used to set and handle various UI events. ## Predefined Action Types The `action` module defines the following predefined action types: | Action | Description | |--------|-------------| | `action.ac_on_text_changed` | Triggered when text content changes | | `action.ac_on_selected` | Triggered when an item is selected | | `action.ac_on_clicked` | Triggered when clicked | | `action.ac_on_resized` | Triggered when resized | | `action.ac_on_scrolled` | Triggered when scrolled | | `action.ac_on_enter` | Triggered when confirmed/entered | | `action.ac_on_load` | Triggered when loaded | | `action.ac_on_save` | Triggered when saved | | `action.ac_on_exit` | Triggered when exiting | ## Usage These action constants are used to set event callback handlers: ```lua import("core.ui.action") import("core.ui.button") -- Create button local btn = button:new("ok", rect{10, 5, 20, 1}, "OK") -- Set click event btn:action_set(action.ac_on_enter, function (v) print("Button clicked!") end) ``` ## action:register * Register custom action types #### Function Prototype ::: tip API ```lua action:register(tag: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tag | Required. Tag name for accumulating count | | ... | Variable arguments. Action constant names | #### Return Value No return value #### Usage Register custom action types: ```lua import("core.ui.action") -- Register custom actions action:register("ac_max", "ac_on_custom1", "ac_on_custom2", "ac_on_custom3" ) -- Now can use these custom actions some_view:action_set(action.ac_on_custom1, function (v) print("Custom action 1 triggered") end) ``` Here is a complete usage example: ```lua import("core.ui.action") import("core.ui.button") import("core.ui.rect") import("core.ui.application") import("core.ui.window") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create window local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "Action Demo") -- Create buttons and set different events local btn1 = button:new("btn1", rect{5, 5, 20, 1}, "< Button1 >") local btn2 = button:new("btn2", rect{30, 5, 20, 1}, "< Button2 >") -- Set different event handlers btn1:action_set(action.ac_on_enter, function (v) print("Button 1 confirmed clicked") end) btn2:action_set(action.ac_on_enter, function (v) print("Button 2 confirmed clicked") end) -- Add buttons to window panel local panel = win:panel() panel:insert(btn1) panel:insert(btn2) -- Select first button panel:select(btn1) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/action.md --- # core.ui.action 此模块定义了 UI 系统的事件动作枚举常量。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.action")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `action` 模块提供了 UI 事件回调动作的类型常量,用于设置和处理各种 UI 事件。 ## 预定义动作类型 `action` 模块定义了以下预定义动作类型: | 动作 | 说明 | |------|------| | `action.ac_on_text_changed` | 文本内容改变时触发 | | `action.ac_on_selected` | 项被选中时触发 | | `action.ac_on_clicked` | 点击时触发 | | `action.ac_on_resized` | 大小调整时触发 | | `action.ac_on_scrolled` | 滚动时触发 | | `action.ac_on_enter` | 确认进入时触发 | | `action.ac_on_load` | 加载时触发 | | `action.ac_on_save` | 保存时触发 | | `action.ac_on_exit` | 退出时触发 | ## 用法说明 这些动作常量用于设置事件回调处理器: ```lua import("core.ui.action") import("core.ui.button") -- 创建按钮 local btn = button:new("ok", rect{10, 5, 20, 1}, "确定") -- 设置点击事件 btn:action_set(action.ac_on_enter, function (v) print("按钮被点击!") end) ``` ## action:register * 注册自定义动作类型 #### 函数原型 ::: tip API ```lua action:register(tag: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tag | 必需。标记名称,用于累加计数 | | ... | 可变参数。动作常量名称 | #### 返回值说明 无返回值 #### 用法说明 注册自定义动作类型: ```lua import("core.ui.action") -- 注册自定义动作 action:register("ac_max", "ac_on_custom1", "ac_on_custom2", "ac_on_custom3" ) -- 现在可以使用这些自定义动作 some_view:action_set(action.ac_on_custom1, function (v) print("自定义动作 1 被触发") end) ``` 以下是一个完整的使用示例: ```lua import("core.ui.action") import("core.ui.button") import("core.ui.rect") import("core.ui.application") import("core.ui.window") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "Action 演示") -- 创建按钮并设置不同的事件 local btn1 = button:new("btn1", rect{5, 5, 20, 1}, "< 按钮1 >") local btn2 = button:new("btn2", rect{30, 5, 20, 1}, "< 按钮2 >") -- 设置不同的事件处理器 btn1:action_set(action.ac_on_enter, function (v) print("按钮 1 被确认点击") end) btn2:action_set(action.ac_on_enter, function (v) print("按钮 2 被确认点击") end) -- 将按钮添加到窗口面板 local panel = win:panel() panel:insert(btn1) panel:insert(btn2) -- 选择第一个按钮 panel:select(btn1) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/application.md --- # core.ui.application This module provides the main application container for the terminal UI system. You can inherit from `application` to implement your own terminal UI applications. ::: tip TIP To use this module, you need to import it first: `import("core.ui.application")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The application module uses inheritance. You create your own application instance and customize its behavior: ```lua import("core.ui.application") local app = application() function app:init() -- Your initialization code end ``` ## application:new * Create a new application instance #### Function Prototype ::: tip API ```lua local app = application() ``` ::: #### Parameter Description No parameters for constructor #### Return Value | Type | Description | |------|-------------| | application | Returns a new application instance | #### Usage Create an application instance that you can customize: ```lua import("core.ui.application") local demo = application() ``` ## application:init * Initialize the application #### Function Prototype ::: tip API ```lua application:init(name: , argv?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Application name string | | argv | Optional. Command line arguments table | #### Return Value No return value #### Usage Initialize your custom application. This is called automatically by `run()`: ```lua function demo:init() application.init(self, "myapp") self:background_set("blue") -- Add your UI components here self:insert(self:main_dialog()) end ``` ## application:run * Run the application and start the event loop #### Function Prototype ::: tip API ```lua application:run(...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ... | Optional. Additional arguments | #### Return Value No return value #### Usage Start your application. This initializes the UI, calls `init()`, and starts the event loop: ```lua local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") self:insert(self:main_dialog()) end -- Start the application demo:run() ``` ## application:background\_set * Set the application background color #### Function Prototype ::: tip API ```lua application:background_set(color: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | color | Required. Color name (e.g., "blue", "red") | #### Return Value No return value #### Usage Set the background color for your application: ```lua self:background_set("blue") ``` ## application:insert * Insert a view into the application #### Function Prototype ::: tip API ```lua application:insert(view: , opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | view | Required. View to insert | | opt | Optional. Options table, supports: `{centerx = true, centery = true}` | #### Return Value No return value #### Usage Add views to your application with optional positioning: ```lua -- Add a dialog self:insert(self:main_dialog()) -- Add a centered dialog self:insert(self:input_dialog(), {centerx = true, centery = true}) ``` ## application:on\_resize * Handle window resize events #### Function Prototype ::: tip API ```lua application:on_resize() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage Override this method to handle resize events and update your UI layout: ```lua function demo:on_resize() self:main_dialog():bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) self:center(self:input_dialog(), {centerx = true, centery = true}) application.on_resize(self) end ``` ## application:menubar * Get the application's menu bar #### Function Prototype ::: tip API ```lua application:menubar() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | menubar | Returns the menu bar instance | #### Usage Access the menu bar component: ```lua local menubar = app:menubar() ``` ## application:desktop * Get the application's desktop area #### Function Prototype ::: tip API ```lua application:desktop() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | desktop | Returns the desktop instance | #### Usage Access the desktop area for placing views: ```lua local desktop = app:desktop() ``` ## application:statusbar * Get the application's status bar #### Function Prototype ::: tip API ```lua application:statusbar() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | statusbar | Returns the status bar instance | #### Usage Access the status bar component: ```lua local statusbar = app:statusbar() statusbar:text_set("Ready") ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/application.md --- # core.ui.application 此模块提供终端 UI 系统的主应用程序容器。您可以继承 `application` 来实现自己的终端 UI 应用程序。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.application")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: application 模块使用继承模式。您创建自己的应用程序实例并自定义其行为: ```lua import("core.ui.application") local app = application() function app:init() -- 您的初始化代码 end ``` ## application:new * 创建新的应用程序实例 #### 函数原型 ::: tip API ```lua local app = application() ``` ::: #### 参数说明 构造函数无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | application | 返回新的应用程序实例 | #### 用法说明 创建一个可以自定义的应用程序实例: ```lua import("core.ui.application") local demo = application() ``` ## application:init * 初始化应用程序 #### 函数原型 ::: tip API ```lua application:init(name: , argv?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。应用程序名称字符串 | | argv | 可选。命令行参数表格 | #### 返回值说明 无返回值 #### 用法说明 初始化您的自定义应用程序。这由 `run()` 自动调用: ```lua function demo:init() application.init(self, "myapp") self:background_set("blue") -- 在这里添加您的 UI 组件 self:insert(self:main_dialog()) end ``` ## application:run * 运行应用程序并启动事件循环 #### 函数原型 ::: tip API ```lua application:run(...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ... | 可选。额外参数 | #### 返回值说明 无返回值 #### 用法说明 启动您的应用程序。这会初始化 UI、调用 `init()` 并启动事件循环: ```lua local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") self:insert(self:main_dialog()) end -- 启动应用程序 demo:run() ``` ## application:background\_set * 设置应用程序背景颜色 #### 函数原型 ::: tip API ```lua application:background_set(color: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | color | 必需。颜色名称(例如:"blue"、"red") | #### 返回值说明 无返回值 #### 用法说明 设置应用程序的背景颜色: ```lua self:background_set("blue") ``` ## application:insert * 将视图插入应用程序 #### 函数原型 ::: tip API ```lua application:insert(view: , opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | view | 必需。要插入的视图 | | opt | 可选。选项表格,支持:`{centerx = true, centery = true}` | #### 返回值说明 无返回值 #### 用法说明 向应用程序添加视图,可选择位置: ```lua -- 添加对话框 self:insert(self:main_dialog()) -- 添加居中的对话框 self:insert(self:input_dialog(), {centerx = true, centery = true}) ``` ## application:on\_resize * 处理窗口大小调整事件 #### 函数原型 ::: tip API ```lua application:on_resize() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 重写此方法以处理调整大小事件并更新您的 UI 布局: ```lua function demo:on_resize() self:main_dialog():bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) self:center(self:input_dialog(), {centerx = true, centery = true}) application.on_resize(self) end ``` ## application:menubar * 获取应用程序的菜单栏 #### 函数原型 ::: tip API ```lua application:menubar() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | menubar | 返回菜单栏实例 | #### 用法说明 访问菜单栏组件: ```lua local menubar = app:menubar() ``` ## application:desktop * 获取应用程序的桌面区域 #### 函数原型 ::: tip API ```lua application:desktop() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | desktop | 返回桌面实例 | #### 用法说明 访问桌面区域以放置视图: ```lua local desktop = app:desktop() ``` ## application:statusbar * 获取应用程序的状态栏 #### 函数原型 ::: tip API ```lua application:statusbar() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | statusbar | 返回状态栏实例 | #### 用法说明 访问状态栏组件,这里是一个完整的自定义应用示例: ```lua import("core.ui.log") import("core.ui.rect") import("core.ui.label") import("core.ui.event") import("core.ui.window") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建主窗口 local win = window:new("window.body", rect{1, 1, self:width() - 1, self:height() - 1}, "主窗口") self:insert(win) -- 创建状态栏 local statusbar = self:statusbar() statusbar:text_set("就绪") end function demo:on_resize() self:desktop():bounds_set(rect{0, 0, self:width(), self:height()}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/boxdialog.md --- # core.ui.boxdialog This module provides a box dialog with a content area for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.boxdialog")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `boxdialog` module extends `textdialog` and provides a box dialog with a content area. It contains a text area with adjustable height and a content box (for placing child views), and serves as the base for complex dialogs like `choicedialog` and `mconfdialog`. ## boxdialog:new * Create a new box dialog instance #### Function Prototype ::: tip API ```lua boxdialog:new(name: , bounds: , title: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Dialog name string | | bounds | Required. Dialog bounds rectangle | | title | Required. Dialog title string | #### Return Value | Type | Description | |------|-------------| | boxdialog | Returns a box dialog instance | #### Usage Create a box dialog: ```lua import("core.ui.boxdialog") import("core.ui.rect") local dialog = boxdialog:new("dialog", rect{1, 1, 80, 25}, "Main Dialog") ``` ## boxdialog:box * Get the content box window #### Function Prototype ::: tip API ```lua boxdialog:box() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | window | Returns the content box window instance | #### Usage Access the content box to add child views: ```lua local box = dialog:box() local panel = box:panel() -- Add child views in panel ``` ## boxdialog:on\_resize * Handle dialog resize #### Function Prototype ::: tip API ```lua boxdialog:on_resize() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage This method is automatically called when the dialog size changes. It relayouts the text area and content box. Here is a complete box dialog example: ```lua import("core.ui.boxdialog") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create box dialog local dialog = boxdialog:new("dialog.main", rect{1, 1, self:width() - 1, self:height() - 1}, "Main Dialog") -- Set instruction text dialog:text():text_set("Use the arrow keys to navigate this window or press the hotkey of the item you wish to select followed by the ") -- Add buttons dialog:button_add("ok", "< OK >", function (v) self:quit() end) dialog:button_add("cancel", "< Cancel >", "cm_quit") self:insert(dialog) self._dialog = dialog end function demo:on_resize() self._dialog:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/boxdialog.md --- # core.ui.boxdialog 此模块为终端 UI 系统提供带内容框的对话框。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.boxdialog")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `boxdialog` 模块继承自 `textdialog`,提供带内容框的对话框。它包含一个可调整高度的文本区域和一个内容框(用于放置子视图),是 `choicedialog` 和 `mconfdialog` 等复杂对话框的基础。 ## boxdialog:new * 创建新的框对话框实例 #### 函数原型 ::: tip API ```lua boxdialog:new(name: , bounds: , title: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。对话框名称字符串 | | bounds | 必需。对话框边界矩形 | | title | 必需。对话框标题字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | boxdialog | 返回框对话框实例 | #### 用法说明 创建框对话框: ```lua import("core.ui.boxdialog") import("core.ui.rect") local dialog = boxdialog:new("dialog", rect{1, 1, 80, 25}, "主对话框") ``` ## boxdialog:box * 获取内容框窗口 #### 函数原型 ::: tip API ```lua boxdialog:box() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | window | 返回内容框窗口实例 | #### 用法说明 访问内容框以添加子视图: ```lua local box = dialog:box() local panel = box:panel() -- 在 panel 中添加子视图 ``` ## boxdialog:on\_resize * 处理对话框大小调整 #### 函数原型 ::: tip API ```lua boxdialog:on_resize() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 当对话框大小改变时自动调用此方法。它会重新调整文本区域和内容框的布局。 以下是一个完整的框对话框示例: ```lua import("core.ui.boxdialog") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建框对话框 local dialog = boxdialog:new("dialog.main", rect{1, 1, self:width() - 1, self:height() - 1}, "主对话框") -- 设置提示文本 dialog:text():text_set("使用方向键导航或按快捷键空格键进行选择") -- 添加按钮 dialog:button_add("ok", "< 确定 >", function (v) self:quit() end) dialog:button_add("cancel", "< 取消 >", "cm_quit") self:insert(dialog) self._dialog = dialog end function demo:on_resize() self._dialog:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/button.md --- # core.ui.button This module provides a clickable button component for the terminal UI. ::: tip TIP To use this module, you need to import it first: `import("core.ui.button")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `button` module extends `label` and provides a clickable button with: * Selectable and focusable support * Mouse click handling * Enter key activation * Visual feedback (reverse video when selected and focused) ## button:new * Create a new button instance #### Function Prototype ::: tip API ```lua button:new(name: , bounds: , text: , on_action?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Button name string | | bounds | Required. Button bounds rectangle | | text | Required. Button text to display | | on\_action | Optional. Function to call when button is activated | #### Return Value | Type | Description | |------|-------------| | button | Returns a button instance | #### Usage Create a button with action handler: ```lua import("core.ui.button") import("core.ui.rect") local btn = button:new("ok", rect{10, 5, 20, 1}, "Ok", function (v) print("Button clicked!") v:parent():quit() end) ``` ## button:text * Get the button text #### Function Prototype ::: tip API ```lua button:text() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the button text | #### Usage Get the button text: ```lua local text = btn:text() print(text) -- Output: Ok ``` ## button:text\_set * Set the button text #### Function Prototype ::: tip API ```lua button:text_set(text: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | text | Required. Button text to display | #### Return Value | Type | Description | |------|-------------| | button | Returns the button instance (for method chaining) | #### Usage Set or update the button text: ```lua btn:text_set("Cancel") btn:text_set("< OK >") -- Can use brackets for visual styling ``` ## button:textattr * Get the text attribute #### Function Prototype ::: tip API ```lua button:textattr() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the text attribute string | #### Usage Get the current text attribute: ```lua local attr = btn:textattr() ``` ## button:textattr\_set * Set the text attribute (color, style) #### Function Prototype ::: tip API ```lua button:textattr_set(attr: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | attr | Required. Text attribute string (e.g., "red bold") | #### Return Value | Type | Description | |------|-------------| | button | Returns the button instance (for method chaining) | #### Usage Set button text color and style: ```lua btn:textattr_set("green") -- Green text btn:textattr_set("yellow bold") -- Yellow bold text btn:textattr_set("cyan onblack") -- Cyan text on black background ``` ## button:action\_set * Set the button action handler #### Function Prototype ::: tip API ```lua button:action_set(action_type: , handler: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | action\_type | Required. Action type (e.g., `action.ac_on_enter`) | | handler | Required. Function to call when action is triggered | #### Return Value | Type | Description | |------|-------------| | button | Returns the button instance (for method chaining) | #### Usage Set custom action handler: ```lua import("core.ui.action") btn:action_set(action.ac_on_enter, function (v) print("Button activated!") end) ``` ## button:on\_event * Handle keyboard events #### Function Prototype ::: tip API ```lua button:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if Enter key was handled, `nil` otherwise | #### Usage This is automatically called when the button receives keyboard events. When selected and Enter is pressed, it triggers the button's action. ## button:on\_draw * Draw the button view #### Function Prototype ::: tip API ```lua button:on_draw(transparent: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | transparent | Optional. Draw with transparency | #### Return Value No return value #### Usage This is automatically called when the button needs to be drawn. The button is displayed with reverse video (inverted colors) when selected and focused. Here's a complete example: ```lua import("core.ui.button") import("core.ui.rect") import("core.ui.application") import("core.ui.window") import("core.ui.action") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create window local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "Button Demo") -- Create buttons local ok_btn = button:new("ok", rect{5, 5, 15, 1}, "< OK >", function (v) print("OK button clicked!") end) local cancel_btn = button:new("cancel", rect{25, 5, 20, 1}, "< Cancel >", function (v) print("Cancel button clicked!") self:quit() end) local apply_btn = button:new("apply", rect{50, 5, 18, 1}, "< Apply >", function (v) print("Apply button clicked!") end) -- Set different text attributes ok_btn:textattr_set("green") cancel_btn:textattr_set("red") apply_btn:textattr_set("cyan") -- Add buttons to window panel local panel = win:panel() panel:insert(ok_btn) panel:insert(cancel_btn) panel:insert(apply_btn) -- Select first button panel:select(ok_btn) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/button.md --- # core.ui.button 此模块提供用于终端 UI 的可点击按钮组件。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.button")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `button` 模块继承自 `label`,提供可点击的按钮,包含: * 可选择和可聚焦支持 * 鼠标点击处理 * Enter 键激活 * 视觉反馈(选中和聚焦时反转显示) ## button:new * 创建新的按钮实例 #### 函数原型 ::: tip API ```lua button:new(name: , bounds: , text: , on_action?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。按钮名称字符串 | | bounds | 必需。按钮边界矩形 | | text | 必需。要显示的按钮文本 | | on\_action | 可选。激活按钮时要调用的函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | button | 返回按钮实例 | #### 用法说明 创建带操作处理器的按钮: ```lua import("core.ui.button") import("core.ui.rect") local btn = button:new("ok", rect{10, 5, 20, 1}, "确定", function (v) print("按钮已点击!") v:parent():quit() end) ``` ## button:text * 获取按钮文本 #### 函数原型 ::: tip API ```lua button:text() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回按钮文本 | #### 用法说明 获取按钮文本: ```lua local text = btn:text() print(text) -- 输出: 确定 ``` ## button:text\_set * 设置按钮文本 #### 函数原型 ::: tip API ```lua button:text_set(text: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | text | 必需。要显示的按钮文本 | #### 返回值说明 | 类型 | 描述 | |------|------| | button | 返回按钮实例(用于链式调用) | #### 用法说明 设置或更新按钮文本: ```lua btn:text_set("取消") btn:text_set("< 确定 >") -- 可使用括号进行视觉样式化 ``` ## button:textattr * 获取文本属性 #### 函数原型 ::: tip API ```lua button:textattr() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回文本属性字符串 | #### 用法说明 获取当前文本属性: ```lua local attr = btn:textattr() ``` ## button:textattr\_set * 设置文本属性(颜色、样式) #### 函数原型 ::: tip API ```lua button:textattr_set(attr: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | attr | 必需。文本属性字符串(例如:"red bold") | #### 返回值说明 | 类型 | 描述 | |------|------| | button | 返回按钮实例(用于链式调用) | #### 用法说明 设置按钮文本颜色和样式: ```lua btn:textattr_set("green") -- 绿色文本 btn:textattr_set("yellow bold") -- 黄色粗体文本 btn:textattr_set("cyan onblack") -- 黑色背景上的青色文本 ``` ## button:action\_set * 设置按钮操作处理器 #### 函数原型 ::: tip API ```lua button:action_set(action_type: , handler: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | action\_type | 必需。操作类型(例如:`action.ac_on_enter`) | | handler | 必需。触发操作时要调用的函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | button | 返回按钮实例(用于链式调用) | #### 用法说明 设置自定义操作处理器: ```lua import("core.ui.action") btn:action_set(action.ac_on_enter, function (v) print("按钮已激活!") end) ``` ## button:on\_event * 处理键盘事件 #### 函数原型 ::: tip API ```lua button:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果 Enter 键被处理返回 `true`,否则返回 `nil` | #### 用法说明 当按钮接收键盘事件时自动调用此方法。当选中按钮并按下 Enter 时,会触发按钮的操作。 ## button:on\_draw * 绘制按钮视图 #### 函数原型 ::: tip API ```lua button:on_draw(transparent: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | transparent | 可选。使用透明度绘制 | #### 返回值说明 无返回值 #### 用法说明 当需要绘制按钮时自动调用此方法。按钮在选中和聚焦时以反转显示(反色)显示。以下是一个完整示例: ```lua import("core.ui.button") import("core.ui.rect") import("core.ui.application") import("core.ui.window") import("core.ui.action") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "按钮演示") -- 创建按钮 local ok_btn = button:new("ok", rect{5, 5, 15, 1}, "< 确定 >", function (v) print("确定按钮已点击!") end) local cancel_btn = button:new("cancel", rect{25, 5, 20, 1}, "< 取消 >", function (v) print("取消按钮已点击!") self:quit() end) local apply_btn = button:new("apply", rect{50, 5, 18, 1}, "< 应用 >", function (v) print("应用按钮已点击!") end) -- 设置不同的文本属性 ok_btn:textattr_set("green") cancel_btn:textattr_set("red") apply_btn:textattr_set("cyan") -- 将按钮添加到窗口面板 local panel = win:panel() panel:insert(ok_btn) panel:insert(cancel_btn) panel:insert(apply_btn) -- 选择第一个按钮 panel:select(ok_btn) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/choicebox.md --- # core.ui.choicebox This module provides a single-choice list box component for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.choicebox")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `choicebox` module extends `panel` and provides a single-choice list box with: * Single selection support (uses `(X)` to mark selected item, `( )` for unselected items) * Keyboard navigation (Up/Down, PageUp/PageDown) * Scroll support (when the number of options exceeds the visible area) * Automatic resize handling * Event callbacks (on\_load, on\_scrolled, on\_selected) ## choicebox:new * Create a new single-choice list box instance #### Function Prototype ::: tip API ```lua choicebox:new(name: , bounds: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. List box name string | | bounds | Required. List box bounds rectangle | #### Return Value | Type | Description | |------|-------------| | choicebox | Returns a single-choice list box instance | #### Usage Create a single-choice list box: ```lua import("core.ui.choicebox") import("core.ui.rect") local choicebox = choicebox:new("my_choice", rect{10, 5, 30, 10}) ``` ## choicebox:load * Load option values into the list box #### Function Prototype ::: tip API ```lua choicebox:load(values:
, selected?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | values | Required. Array of option values | | selected | Optional. Index of the default selected item (1-based), defaults to 1 | #### Return Value No return value #### Usage Load options and set the default selected item: ```lua local values = {"Option 1", "Option 2", "Option 3", "Option 4"} choicebox:load(values, 2) -- Default to the second item ``` After loading, the `action.ac_on_load` event is triggered. ## choicebox:scrollable * Check if the list box is scrollable #### Function Prototype ::: tip API ```lua choicebox:scrollable() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` when the number of options exceeds the visible height, otherwise returns `false` | #### Usage Check if scrolling is needed: ```lua if choicebox:scrollable() then print("Many options available, can scroll to view") end ``` ## choicebox:scroll * Scroll the list box content #### Function Prototype ::: tip API ```lua choicebox:scroll(count: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | count | Required. Number of lines to scroll, positive for down, negative for up | #### Return Value No return value #### Usage Scroll the list box content: ```lua choicebox:scroll(1) -- Scroll down one line choicebox:scroll(-1) -- Scroll up one line choicebox:scroll(choicebox:height()) -- Scroll down one page ``` When scrolling, the `action.ac_on_scrolled` event is triggered, passing the current scroll position ratio (0.0-1.0). ## choicebox:on\_event * Handle keyboard events #### Function Prototype ::: tip API ```lua choicebox:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if the event was handled, otherwise returns `false` | #### Usage The list box automatically handles the following keyboard events: * `Up`: Navigate up, automatically scrolls up one page when reaching the top * `Down`: Navigate down, automatically scrolls down one page when reaching the bottom * `PageUp`: Scroll up one page * `PageDown`: Scroll down one page * `Enter` or `Space`: Select the current item When an item is selected, the `action.ac_on_selected` event is triggered, passing the selected item's index and value. Override this method to add custom event handling: ```lua function my_choicebox:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Tab" then -- Custom handling for Tab key return true end return choicebox.on_event(self, e) end ``` ## choicebox:on\_resize * Handle list box resize #### Function Prototype ::: tip API ```lua choicebox:on_resize() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage This method is automatically called when the list box size changes. It relayouts the option items, only showing items within the currently visible range. Here is a complete usage example: ```lua import("core.ui.choicebox") import("core.ui.rect") import("core.ui.window") import("core.ui.application") import("core.ui.action") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create window local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "Choice Box Demo") -- Create single-choice list box local choice = choicebox:new("choices", rect{5, 5, 40, 15}) -- Load option values local options = {"gcc", "clang", "msvc", "mingw", "icc"} choice:load(options, 1) -- Default to first item -- Set selection event handler choice:action_set(action.ac_on_selected, function (v, index, value) print("Selected:", value, "(index:", index, ")") end) -- Set scroll event handler choice:action_set(action.ac_on_scrolled, function (v, ratio) print("Scroll position:", string.format("%.2f%%", ratio * 100)) end) -- Add list box to window panel local panel = win:panel() panel:insert(choice) -- Select the list box panel:select(choice) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/choicebox.md --- # core.ui.choicebox 此模块为终端 UI 系统提供单选列表框组件。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.choicebox")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `choicebox` 模块继承自 `panel`,提供单选列表框功能,包含: * 单选模式支持(使用 `(X)` 标记选中项,`( )` 标记未选中项) * 键盘导航(Up/Down、PageUp/PageDown) * 滚动支持(当选项数量超过显示区域时) * 自动调整大小 * 事件回调(on\_load、on\_scrolled、on\_selected) ## choicebox:new * 创建新的单选列表框实例 #### 函数原型 ::: tip API ```lua choicebox:new(name: , bounds: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。列表框名称字符串 | | bounds | 必需。列表框边界矩形 | #### 返回值说明 | 类型 | 描述 | |------|------| | choicebox | 返回单选列表框实例 | #### 用法说明 创建单选列表框: ```lua import("core.ui.choicebox") import("core.ui.rect") local choicebox = choicebox:new("my_choice", rect{10, 5, 30, 10}) ``` ## choicebox:load * 加载选项值到列表框中 #### 函数原型 ::: tip API ```lua choicebox:load(values:
, selected?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | values | 必需。选项值数组 | | selected | 可选。默认选中项的索引(从 1 开始),默认为 1 | #### 返回值说明 无返回值 #### 用法说明 加载选项并设置默认选中项: ```lua local values = {"选项1", "选项2", "选项3", "选项4"} choicebox:load(values, 2) -- 默认选中第二项 ``` 加载完成后会触发 `action.ac_on_load` 事件。 ## choicebox:scrollable * 检查列表框是否可滚动 #### 函数原型 ::: tip API ```lua choicebox:scrollable() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 当选项数量超过显示高度时返回 `true`,否则返回 `false` | #### 用法说明 检查是否需要滚动: ```lua if choicebox:scrollable() then print("选项数量较多,可以滚动查看") end ``` ## choicebox:scroll * 滚动列表框内容 #### 函数原型 ::: tip API ```lua choicebox:scroll(count: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | count | 必需。滚动行数,正数向下滚动,负数向上滚动 | #### 返回值说明 无返回值 #### 用法说明 滚动列表框内容: ```lua choicebox:scroll(1) -- 向下滚动一行 choicebox:scroll(-1) -- 向上滚动一行 choicebox:scroll(choicebox:height()) -- 向下滚动一页 ``` 滚动时会触发 `action.ac_on_scrolled` 事件,传递当前滚动位置比例(0.0-1.0)。 ## choicebox:on\_event * 处理键盘事件 #### 函数原型 ::: tip API ```lua choicebox:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件已处理返回 `true`,否则返回 `false` | #### 用法说明 列表框自动处理以下键盘事件: * `Up`:向上导航,到达顶部时自动向上滚动一页 * `Down`:向下导航,到达底部时自动向下滚动一页 * `PageUp`:向上滚动一页 * `PageDown`:向下滚动一页 * `Enter` 或 `Space`:选中当前项 选中项时会触发 `action.ac_on_selected` 事件,传递选中项的索引和值。 重写此方法以添加自定义事件处理: ```lua function my_choicebox:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Tab" then -- 自定义处理 Tab 键 return true end return choicebox.on_event(self, e) end ``` ## choicebox:on\_resize * 处理列表框大小调整 #### 函数原型 ::: tip API ```lua choicebox:on_resize() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 当列表框大小改变时自动调用此方法。它会重新布局选项项,只显示当前可见范围内的项。 以下是一个完整的使用示例: ```lua import("core.ui.choicebox") import("core.ui.rect") import("core.ui.window") import("core.ui.application") import("core.ui.action") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "单选列表框演示") -- 创建单选列表框 local choice = choicebox:new("choices", rect{5, 5, 40, 15}) -- 加载选项值 local options = {"gcc", "clang", "msvc", "mingw", "icc"} choice:load(options, 1) -- 默认选中第一项 -- 设置选中事件处理 choice:action_set(action.ac_on_selected, function (v, index, value) print("已选择:", value, "(索引:", index, ")") end) -- 设置滚动事件处理 choice:action_set(action.ac_on_scrolled, function (v, ratio) print("滚动位置:", string.format("%.2f%%", ratio * 100)) end) -- 将列表框添加到窗口面板 local panel = win:panel() panel:insert(choice) -- 选择列表框 panel:select(choice) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/choicedialog.md --- # core.ui.choicedialog This module provides a single-choice selection dialog for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.choicedialog")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `choicedialog` module extends `boxdialog` and provides a single-choice selection dialog. It contains a `choicebox` for displaying options and a scrollbar for long lists. ## choicedialog:new * Create a new choice dialog instance #### Function Prototype ::: tip API ```lua choicedialog:new(name: , bounds: , title: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Dialog name string | | bounds | Required. Dialog bounds rectangle | | title | Required. Dialog title string | #### Return Value | Type | Description | |------|-------------| | choicedialog | Returns a choice dialog instance | #### Usage Create a choice dialog: ```lua import("core.ui.choicedialog") import("core.ui.rect") local dialog = choicedialog:new("choice", rect{10, 5, 50, 20}, "Select Option") ``` ## choicedialog:choicebox * Get the choice list box #### Function Prototype ::: tip API ```lua choicedialog:choicebox() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | choicebox | Returns the choice list box instance | #### Usage Access the choice list box to load options: ```lua local choicebox = dialog:choicebox() choicebox:load({"Option 1", "Option 2", "Option 3"}, 1) ``` ## choicedialog:scrollbar\_box * Get the scrollbar component #### Function Prototype ::: tip API ```lua choicedialog:scrollbar_box() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns the scrollbar instance | #### Usage Access the scrollbar component for customization: ```lua local scrollbar = dialog:scrollbar_box() scrollbar:show(true) ``` ## choicedialog:on\_event * Handle dialog events #### Function Prototype ::: tip API ```lua choicedialog:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if the event was handled, otherwise returns `false` | #### Usage The dialog automatically handles the following events: * `Up/Down`: Navigate options * `Space`: Confirm selection Here is a complete choice dialog example: ```lua import("core.ui.choicedialog") import("core.ui.rect") import("core.ui.event") import("core.ui.application") import("core.ui.action") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create choice dialog local dialog = choicedialog:new("choice", rect{1, 1, self:width() - 1, self:height() - 1}, "Select Compiler") -- Load options local options = {"gcc", "clang", "msvc"} dialog:choicebox():load(options, 1) -- Set selection event dialog:choicebox():action_set(action.ac_on_selected, function (v, index, value) print("Selected:", value, "(index:", index, ")") end) -- Handle select button dialog:button("select"):action_set(action.ac_on_enter, function (v, e) local current = dialog:choicebox():current() if current then local value = current:extra("value") local index = current:extra("index") print("Final choice:", value, "(index:", index, ")") end self:quit() end) self:insert(dialog) self._dialog = dialog end function demo:on_resize() self._dialog:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/choicedialog.md --- # core.ui.choicedialog 此模块为终端 UI 系统提供单选选项对话框。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.choicedialog")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `choicedialog` 模块继承自 `boxdialog`,提供单选选项对话框。它包含一个 `choicebox` 用于显示可选项和一个滚动条用于长列表。 ## choicedialog:new * 创建新的选择对话框实例 #### 函数原型 ::: tip API ```lua choicedialog:new(name: , bounds: , title: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。对话框名称字符串 | | bounds | 必需。对话框边界矩形 | | title | 必需。对话框标题字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | choicedialog | 返回选择对话框实例 | #### 用法说明 创建选择对话框: ```lua import("core.ui.choicedialog") import("core.ui.rect") local dialog = choicedialog:new("choice", rect{10, 5, 50, 20}, "选择选项") ``` ## choicedialog:choicebox * 获取选项列表框 #### 函数原型 ::: tip API ```lua choicedialog:choicebox() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | choicebox | 返回选项列表框实例 | #### 用法说明 访问选项列表框以加载选项: ```lua local choicebox = dialog:choicebox() choicebox:load({"选项1", "选项2", "选项3"}, 1) ``` ## choicedialog:scrollbar\_box * 获取滚动条组件 #### 函数原型 ::: tip API ```lua choicedialog:scrollbar_box() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例 | #### 用法说明 访问滚动条组件进行自定义: ```lua local scrollbar = dialog:scrollbar_box() scrollbar:show(true) ``` ## choicedialog:on\_event * 处理对话框事件 #### 函数原型 ::: tip API ```lua choicedialog:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件已处理返回 `true`,否则返回 `false` | #### 用法说明 对话框自动处理以下事件: * `Up/Down`:导航选项 * `Space`:确认选择 以下是一个完整的选择对话框示例: ```lua import("core.ui.choicedialog") import("core.ui.rect") import("core.ui.event") import("core.ui.application") import("core.ui.action") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建选择对话框 local dialog = choicedialog:new("choice", rect{1, 1, self:width() - 1, self:height() - 1}, "选择编译器") -- 加载选项 local options = {"gcc", "clang", "msvc"} dialog:choicebox():load(options, 1) -- 设置选中事件 dialog:choicebox():action_set(action.ac_on_selected, function (v, index, value) print("已选择:", value, "(索引:", index, ")") end) -- 处理选择按钮 dialog:button("select"):action_set(action.ac_on_enter, function (v, e) local current = dialog:choicebox():current() if current then local value = current:extra("value") local index = current:extra("index") print("最终选择:", value, "(索引:", index, ")") end self:quit() end) self:insert(dialog) self._dialog = dialog end function demo:on_resize() self._dialog:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/dialog.md --- # core.ui.dialog This module provides the base dialog class for all dialog components in the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.dialog")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `dialog` module is the base class for all dialog components. It extends the window class and provides button management functionality for creating interactive dialogs. ## dialog:new * Create a new dialog instance #### Function Prototype ::: tip API ```lua dialog:new(name: , bounds: , title: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Dialog name string | | bounds | Required. Dialog bounds rectangle | | title | Required. Dialog title string | #### Return Value | Type | Description | |------|-------------| | dialog | Returns a dialog instance | #### Usage Create a dialog with name, bounds, and title: ```lua import("core.ui.dialog") import("core.ui.rect") local dialog = dialog:new("mydialog", rect{1, 1, 50, 20}, "My Dialog") ``` ## dialog:buttons * Get the buttons panel #### Function Prototype ::: tip API ```lua dialog:buttons() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | panel | Returns the buttons panel | #### Usage Access the buttons panel for managing dialog buttons: ```lua local buttons = dialog:buttons() ``` ## dialog:button * Get a specific button by name #### Function Prototype ::: tip API ```lua dialog:button(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Button name string | #### Return Value | Type | Description | |------|-------------| | button | Returns the button instance, nil if not found | #### Usage Get a specific button from the dialog: ```lua local quit_btn = dialog:button("quit") ``` ## dialog:button\_add * Add a button to the dialog #### Function Prototype ::: tip API ```lua dialog:button_add(name: , text: , command: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Button name string | | text | Required. Button display text | | command | Required. Command string or function to execute when clicked | #### Return Value | Type | Description | |------|-------------| | button | Returns the created button instance | #### Usage Add buttons to the dialog: ```lua -- Add button with string command dialog:button_add("quit", "< Quit >", "cm_quit") -- Add button with function callback dialog:button_add("save", "< Save >", function (v) print("Save clicked") dialog:show(false) end) ``` ## dialog:button\_select * Select a button by name #### Function Prototype ::: tip API ```lua dialog:button_select(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Button name to select | #### Return Value | Type | Description | |------|-------------| | dialog | Returns the dialog instance | #### Usage Select a button programmatically: ```lua dialog:button_select("quit") ``` ## dialog:show * Show or hide the dialog #### Function Prototype ::: tip API ```lua dialog:show(visible: , opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | visible | Required. Show or hide the dialog | | opt | Optional. Options table, supports: `{focused = true}` | #### Return Value No return value #### Usage Show or hide the dialog: ```lua dialog:show(true) -- Show the dialog dialog:show(false) -- Hide the dialog -- Show with focus dialog:show(true, {focused = true}) ``` ## dialog:quit * Close the dialog #### Function Prototype ::: tip API ```lua dialog:quit() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage Close and remove the dialog from its parent: ```lua dialog:quit() ``` ## dialog:on\_event * Handle dialog events #### Function Prototype ::: tip API ```lua dialog:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if event was handled, false otherwise | #### Usage The dialog automatically handles Esc key to close. Override this method for custom event handling: ```lua function my_dialog:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Enter" then self:quit() return true end return dialog.on_event(self, e) end ``` ## dialog:background\_set * Set dialog background color #### Function Prototype ::: tip API ```lua dialog:background_set(color: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | color | Required. Color name (e.g., "blue", "red") | #### Return Value No return value #### Usage Set the background color: ```lua dialog:background_set("blue") ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/dialog.md --- # core.ui.dialog 此模块为终端 UI 系统提供基础对话框类。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.dialog")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `dialog` 模块是所有对话框组件的基础类。它继承自窗口类,并提供按钮管理功能,用于创建交互式对话框。 ## dialog:new * 创建新的对话框实例 #### 函数原型 ::: tip API ```lua dialog:new(name: , bounds: , title: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。对话框名称字符串 | | bounds | 必需。对话框边界矩形 | | title | 必需。对话框标题字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | dialog | 返回对话框实例 | #### 用法说明 创建一个具有名称、边界和标题的对话框: ```lua import("core.ui.dialog") import("core.ui.rect") local dialog = dialog:new("mydialog", rect{1, 1, 50, 20}, "我的对话框") ``` ## dialog:buttons * 获取按钮面板 #### 函数原型 ::: tip API ```lua dialog:buttons() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | panel | 返回按钮面板 | #### 用法说明 访问按钮面板以管理对话框按钮: ```lua local buttons = dialog:buttons() ``` ## dialog:button * 根据名称获取特定按钮 #### 函数原型 ::: tip API ```lua dialog:button(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。按钮名称字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | button | 返回按钮实例,未找到则返回 nil | #### 用法说明 从对话框获取特定按钮: ```lua local quit_btn = dialog:button("quit") ``` ## dialog:button\_add * 向对话框添加按钮 #### 函数原型 ::: tip API ```lua dialog:button_add(name: , text: , command: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。按钮名称字符串 | | text | 必需。按钮显示文本 | | command | 必需。单击时执行的命令字符串或函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | button | 返回创建的按钮实例 | #### 用法说明 向对话框添加按钮: ```lua -- 添加带字符串命令的按钮 dialog:button_add("quit", "< 退出 >", "cm_quit") -- 添加带函数回调的按钮 dialog:button_add("save", "< 保存 >", function (v) print("保存被点击") dialog:show(false) end) ``` ## dialog:button\_select * 通过名称选择按钮 #### 函数原型 ::: tip API ```lua dialog:button_select(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。要选择的按钮名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | dialog | 返回对话框实例 | #### 用法说明 以编程方式选择按钮: ```lua dialog:button_select("quit") ``` ## dialog:show * 显示或隐藏对话框 #### 函数原型 ::: tip API ```lua dialog:show(visible: , opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | visible | 必需。显示或隐藏对话框 | | opt | 可选。选项表格,支持:`{focused = true}` | #### 返回值说明 无返回值 #### 用法说明 显示或隐藏对话框: ```lua dialog:show(true) -- 显示对话框 dialog:show(false) -- 隐藏对话框 -- 带焦点显示 dialog:show(true, {focused = true}) ``` ## dialog:quit * 关闭对话框 #### 函数原型 ::: tip API ```lua dialog:quit() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 关闭并从其父容器中移除对话框: ```lua dialog:quit() ``` ## dialog:on\_event * 处理对话框事件 #### 函数原型 ::: tip API ```lua dialog:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 事件被处理返回 true,否则返回 false | #### 用法说明 对话框自动处理 Esc 键关闭。重写此方法以进行自定义事件处理: ```lua function my_dialog:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Enter" then self:quit() return true end return dialog.on_event(self, e) end ``` ## dialog:background\_set * 设置对话框背景颜色 #### 函数原型 ::: tip API ```lua dialog:background_set(color: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | color | 必需。颜色名称(例如:"blue"、"red") | #### 返回值说明 无返回值 #### 用法说明 设置背景颜色,这里是一个完整的使用示例: ```lua import("core.ui.log") import("core.ui.rect") import("core.ui.label") import("core.ui.event") import("core.ui.boxdialog") import("core.ui.textdialog") import("core.ui.inputdialog") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建主对话框 local dialog_main = boxdialog:new("dialog.main", rect{1, 1, self:width() - 1, self:height() - 1}, "主对话框") dialog_main:text():text_set("示例对话框内容") dialog_main:button_add("ok", "< 确定 >", function (v) self:quit() end) dialog_main:button_add("cancel", "< 取消 >", "cm_quit") self:insert(dialog_main) end function demo:on_resize() self:dialog_main():bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/event.md --- # core.ui.event This module provides event types and classes for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.event")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `event` module provides a unified way to handle different types of events in the terminal UI, including keyboard events, mouse events, command events, text events, and idle events. ## Event Types The module defines the following event types: | Event Type | Value | Description | |-----------|-------|-------------| | event.ev\_keyboard | 1 | Keyboard key press/release event | | event.ev\_mouse | 2 | Mouse button/movement event | | event.ev\_command | 3 | Command event | | event.ev\_text | 4 | Text input event | | event.ev\_idle | 5 | Idle event (no user input) | | event.ev\_max | 5 | Maximum event type value | ## Command Event Types The module defines the following command event types: | Command Type | Value | Description | |-------------|-------|-------------| | event.cm\_quit | 1 | Quit application | | event.cm\_exit | 2 | Exit current view | | event.cm\_enter | 3 | Enter/confirm action | | event.cm\_max | 3 | Maximum command type value | ## event:is\_key * Check if event is a keyboard event with specific key #### Function Prototype ::: tip API ```lua event:is_key(key_name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key\_name | Required. Name of the key to check (e.g., "Enter", "Esc", "Tab") | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if event is keyboard event for specified key, `false` otherwise | #### Usage Check if the event is a specific keyboard event: ```lua import("core.ui.event") function view:on_event(e) if e:is_key("Enter") then print("Enter key pressed") return true -- Event handled elseif e:is_key("Esc") then self:quit() return true end return false end ``` ## event:is\_command * Check if event is a command event with specific command #### Function Prototype ::: tip API ```lua event:is_command(command: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | command | Required. Command name to check (e.g., "cm\_quit", "cm\_enter") | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if event is command event for specified command, `false` otherwise | #### Usage Check if the event is a specific command event: ```lua import("core.ui.event") function view:on_event(e) if e:is_command("cm_quit") then self:quit() return true elseif e:is_command("cm_enter") then self:on_confirm() return true end return false end ``` ## event:dump * Dump event information for debugging #### Function Prototype ::: tip API ```lua event:dump() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage Print event information for debugging: ```lua import("core.ui.event") function view:on_event(e) e:dump() -- Output: event(key): Enter 10 .. or event(cmd): cm_quit .. end ``` ## Event Objects ### event.keyboard * Create a keyboard event object #### Function Prototype ::: tip API ```lua event.keyboard(key_code: , key_name: , key_meta: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key\_code | Required. Numeric key code | | key\_name | Required. Key name string (e.g., "Enter", "Esc", "Tab") | | key\_meta | Required. `true` if ALT key was pressed, `false` otherwise | #### Return Value | Type | Description | |------|-------------| | event | Returns a keyboard event object with `type = event.ev_keyboard` | #### Usage Create a keyboard event (typically done internally): ```lua import("core.ui.event") -- This is typically created by the UI framework -- when a key is pressed, but you can create one manually: local e = event.keyboard(10, "Enter", false) ``` ### event.mouse * Create a mouse event object #### Function Prototype ::: tip API ```lua event.mouse(btn_code: , x: , y: , btn_name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | btn\_code | Required. Mouse button event code | | x | Required. X coordinate | | y | Required. Y coordinate | | btn\_name | Required. Button name string | #### Return Value | Type | Description | |------|-------------| | event | Returns a mouse event object with `type = event.ev_mouse` | #### Usage Create a mouse event (typically done internally): ```lua import("core.ui.event") -- This is typically created by the UI framework -- when mouse is used, but you can create one manually: local e = event.mouse(1, 10, 20, "btn_left") ``` ### event.command * Create a command event object #### Function Prototype ::: tip API ```lua event.command(command: , extra?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | command | Required. Command name string | | extra | Optional. Additional data table | #### Return Value | Type | Description | |------|-------------| | event | Returns a command event object with `type = event.ev_command` | #### Usage Create and send a command event: ```lua import("core.ui.event") import("core.ui.action") -- Create a quit command event local e = event.command("cm_quit") -- Send the event to the view view:on_event(e) ``` ### event.idle * Create an idle event object #### Function Prototype ::: tip API ```lua event.idle() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | event | Returns an idle event object with `type = event.ev_idle` | #### Usage Create an idle event (typically done internally): ```lua import("core.ui.event") -- This is typically created by the UI framework -- when there's no user input: local e = event.idle() ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/event.md --- # core.ui.event 此模块提供用于终端 UI 系统的事件类型和类。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.event")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `event` 模块提供了在终端 UI 中处理不同类型事件的统一方式,包括键盘事件、鼠标事件、命令事件、文本事件和空闲事件。 ## 事件类型 该模块定义了以下事件类型: | 事件类型 | 值 | 描述 | |---------|-----|------| | event.ev\_keyboard | 1 | 键盘按键按下/释放事件 | | event.ev\_mouse | 2 | 鼠标按键/移动事件 | | event.ev\_command | 3 | 命令事件 | | event.ev\_text | 4 | 文本输入事件 | | event.ev\_idle | 5 | 空闲事件(无用户输入) | | event.ev\_max | 5 | 最大事件类型值 | ## 命令事件类型 该模块定义了以下命令事件类型: | 命令类型 | 值 | 描述 | |---------|-----|------| | event.cm\_quit | 1 | 退出应用程序 | | event.cm\_exit | 2 | 退出当前视图 | | event.cm\_enter | 3 | 进入/确认操作 | | event.cm\_max | 3 | 最大命令类型值 | ## event:is\_key * 检查事件是否是指定键的键盘事件 #### 函数原型 ::: tip API ```lua event:is_key(key_name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key\_name | 必需。要检查的键名(例如:"Enter"、"Esc"、"Tab") | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件是指定键的键盘事件返回 `true`,否则返回 `false` | #### 用法说明 检查事件是否为特定的键盘事件: ```lua import("core.ui.event") function view:on_event(e) if e:is_key("Enter") then print("按下了 Enter 键") return true -- 事件已处理 elseif e:is_key("Esc") then self:quit() return true end return false end ``` ## event:is\_command * 检查事件是否是指定命令的命令事件 #### 函数原型 ::: tip API ```lua event:is_command(command: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | command | 必需。要检查的命令名称(例如:"cm\_quit"、"cm\_enter") | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件是指定命令的命令事件返回 `true`,否则返回 `false` | #### 用法说明 检查事件是否为特定的命令事件: ```lua import("core.ui.event") function view:on_event(e) if e:is_command("cm_quit") then self:quit() return true elseif e:is_command("cm_enter") then self:on_confirm() return true end return false end ``` ## event:dump * 打印事件信息用于调试 #### 函数原型 ::: tip API ```lua event:dump() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 打印事件信息用于调试: ```lua import("core.ui.event") function view:on_event(e) e:dump() -- 输出: event(key): Enter 10 .. 或 event(cmd): cm_quit .. end ``` ## Event 对象 ### event.keyboard * 创建键盘事件对象 #### 函数原型 ::: tip API ```lua event.keyboard(key_code: , key_name: , key_meta: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key\_code | 必需。数字键码 | | key\_name | 必需。键名字符串(例如:"Enter"、"Esc"、"Tab") | | key\_meta | 必需。如果按下了 ALT 键则为 `true`,否则为 `false` | #### 返回值说明 | 类型 | 描述 | |------|------| | event | 返回一个 `type = event.ev_keyboard` 的键盘事件对象 | #### 用法说明 创建键盘事件(通常由框架内部完成): ```lua import("core.ui.event") -- 这通常由 UI 框架在按键时创建 -- 但您也可以手动创建一个: local e = event.keyboard(10, "Enter", false) ``` ### event.mouse * 创建鼠标事件对象 #### 函数原型 ::: tip API ```lua event.mouse(btn_code: , x: , y: , btn_name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | btn\_code | 必需。鼠标按钮事件码 | | x | 必需。X 坐标 | | y | 必需。Y 坐标 | | btn\_name | 必需。按钮名称字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | event | 返回一个 `type = event.ev_mouse` 的鼠标事件对象 | #### 用法说明 创建鼠标事件(通常由框架内部完成): ```lua import("core.ui.event") -- 这通常由 UI 框架在使用鼠标时创建 -- 但您也可以手动创建一个: local e = event.mouse(1, 10, 20, "btn_left") ``` ### event.command * 创建命令事件对象 #### 函数原型 ::: tip API ```lua event.command(command: , extra?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | command | 必需。命令名称字符串 | | extra | 可选。附加数据表格 | #### 返回值说明 | 类型 | 描述 | |------|------| | event | 返回一个 `type = event.ev_command` 的命令事件对象 | #### 用法说明 创建并发送命令事件: ```lua import("core.ui.event") import("core.ui.action") -- 创建一个退出命令事件 local e = event.command("cm_quit") -- 将事件发送给视图 view:on_event(e) ``` ### event.idle * 创建空闲事件对象 #### 函数原型 ::: tip API ```lua event.idle() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | event | 返回一个 `type = event.ev_idle` 的空闲事件对象 | #### 用法说明 创建空闲事件(通常由框架内部完成): ```lua import("core.ui.event") -- 这通常由 UI 框架在无用户输入时创建: local e = event.idle() ``` 以下是一个完整的事件处理示例: ```lua import("core.ui.event") import("core.ui.rect") import("core.ui.label") import("core.ui.window") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("black") -- 创建主体窗口 local body = window:new("window.body", rect{1, 1, self:width() - 1, self:height() - 1}, "主窗口") self:insert(body) -- 创建标签显示事件信息 local label = label:new('demo.label', rect{0, 0, 40, 5}, '这是一个测试') body:panel():insert(label) self._label = label end function demo:on_resize() self:body_window():bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end -- 处理事件 function demo:on_event(e) if e.type < event.ev_max then -- 显示事件信息 local event_info = string.format('type: %s; name: %s; code: %s; meta: %s', tostring(e.type), tostring(e.key_name or e.btn_name or 'none'), tostring(e.key_code or e.x or 0), tostring(e.key_meta or e.y or 0)) self._label:text_set(event_info) end -- 处理特定按键 if e:is_key("Esc") then self:quit() return true elseif e:is_key("Enter") then print("按下了 Enter 键!") return true end application.on_event(self, e) end function demo:body_window() return self:view("window.body") end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/inputdialog.md --- # core.ui.inputdialog This module provides an input dialog for text input in the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.inputdialog")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `inputdialog` module extends textdialog and provides a text input field for user input. It includes a prompt label and a text edit component. ## inputdialog:new * Create a new input dialog instance #### Function Prototype ::: tip API ```lua inputdialog:new(name: , bounds: , title: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Input dialog name string | | bounds | Required. Input dialog bounds rectangle | | title | Required. Input dialog title string | #### Return Value | Type | Description | |------|-------------| | inputdialog | Returns an input dialog instance | #### Usage Create an input dialog with name, bounds, and title: ```lua import("core.ui.inputdialog") import("core.ui.rect") local dialog = inputdialog:new("input", rect{10, 5, 50, 10}, "Input") ``` ## inputdialog:textedit * Get the text edit component #### Function Prototype ::: tip API ```lua inputdialog:textedit() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | textedit | Returns the text edit instance | #### Usage Access the text edit component to get or set input value: ```lua local textedit = dialog:textedit() local input_value = textedit:text() ``` ## inputdialog:text * Get the prompt label #### Function Prototype ::: tip API ```lua inputdialog:text() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | label | Returns the prompt label instance | #### Usage Get and customize the prompt label: ```lua local label = dialog:text() label:text_set("Please enter your name:") label:textattr_set("red") ``` ## inputdialog:button\_add * Add a button to the dialog #### Function Prototype ::: tip API ```lua inputdialog:button_add(name: , text: , command: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Button name string | | text | Required. Button display text | | command | Required. Command string or function to execute | #### Return Value | Type | Description | |------|-------------| | button | Returns the button instance | #### Usage Add buttons to handle user input: ```lua -- Yes button - get input and process dialog:button_add("yes", "< Yes >", function (v) local input = dialog:textedit():text() print("User entered:", input) dialog:quit() end) -- No/Cancel button dialog:button_add("no", "< No >", function (v) dialog:quit() end) ``` ## inputdialog:show * Show or hide the dialog #### Function Prototype ::: tip API ```lua inputdialog:show(visible: , opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | visible | Required. Show or hide the dialog | | opt | Optional. Options table, supports: `{focused = true}` | #### Return Value No return value #### Usage Show or hide the input dialog: ```lua dialog:show(true) -- Show the dialog -- Show with focus dialog:show(true, {focused = true}) ``` ## inputdialog:background\_set * Set dialog background color #### Function Prototype ::: tip API ```lua inputdialog:background_set(color: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | color | Required. Color name (e.g., "blue", "cyan") | #### Return Value No return value #### Usage Set the background color for the dialog and its frame: ```lua dialog:background_set("blue") dialog:frame():background_set("cyan") ``` ## inputdialog:quit * Close the input dialog #### Function Prototype ::: tip API ```lua inputdialog:quit() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage Close and remove the dialog from its parent: ```lua dialog:quit() ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/inputdialog.md --- # core.ui.inputdialog 此模块提供用于终端 UI 系统中的文本输入对话框。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.inputdialog")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `inputdialog` 模块继承自 textdialog,提供用于用户输入的文本输入字段。它包含一个提示标签和一个文本编辑组件。 ## inputdialog:new * 创建新的输入对话框实例 #### 函数原型 ::: tip API ```lua inputdialog:new(name: , bounds: , title: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。输入对话框名称字符串 | | bounds | 必需。输入对话框边界矩形 | | title | 必需。输入对话框标题字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | inputdialog | 返回输入对话框实例 | #### 用法说明 创建一个具有名称、边界和标题的输入对话框: ```lua import("core.ui.inputdialog") import("core.ui.rect") local dialog = inputdialog:new("input", rect{10, 5, 50, 10}, "输入") ``` ## inputdialog:textedit * 获取文本编辑组件 #### 函数原型 ::: tip API ```lua inputdialog:textedit() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | textedit | 返回文本编辑实例 | #### 用法说明 访问文本编辑组件以获取或设置输入值: ```lua local textedit = dialog:textedit() local input_value = textedit:text() ``` ## inputdialog:text * 获取提示标签 #### 函数原型 ::: tip API ```lua inputdialog:text() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | label | 返回提示标签实例 | #### 用法说明 获取并自定义提示标签: ```lua local label = dialog:text() label:text_set("请输入您的姓名:") label:textattr_set("red") ``` ## inputdialog:button\_add * 向对话框添加按钮 #### 函数原型 ::: tip API ```lua inputdialog:button_add(name: , text: , command: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。按钮名称字符串 | | text | 必需。按钮显示文本 | | command | 必需。要执行的命令字符串或函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | button | 返回按钮实例 | #### 用法说明 添加按钮以处理用户输入: ```lua -- 是按钮 - 获取输入并处理 dialog:button_add("yes", "< 是 >", function (v) local input = dialog:textedit():text() print("用户输入:", input) dialog:quit() end) -- 否/取消按钮 dialog:button_add("no", "< 否 >", function (v) dialog:quit() end) ``` ## inputdialog:show * 显示或隐藏对话框 #### 函数原型 ::: tip API ```lua inputdialog:show(visible: , opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | visible | 必需。显示或隐藏对话框 | | opt | 可选。选项表格,支持:`{focused = true}` | #### 返回值说明 无返回值 #### 用法说明 显示或隐藏输入对话框: ```lua dialog:show(true) -- 显示对话框 -- 带焦点显示 dialog:show(true, {focused = true}) ``` ## inputdialog:background\_set * 设置对话框背景颜色 #### 函数原型 ::: tip API ```lua inputdialog:background_set(color: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | color | 必需。颜色名称(例如:"blue"、"cyan") | #### 返回值说明 无返回值 #### 用法说明 设置对话框及其边框的背景颜色: ```lua dialog:background_set("blue") dialog:frame():background_set("cyan") ``` ## inputdialog:quit * 关闭输入对话框 #### 函数原型 ::: tip API ```lua inputdialog:quit() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 关闭并从其父容器中移除对话框: ```lua dialog:quit() ``` 以下是一个完整的输入对话框使用示例: ```lua import("core.ui.inputdialog") import("core.ui.rect") import("core.ui.application") local app = application() function app:init() application.init(self, "demo") self:background_set("blue") -- 创建输入对话框 local dialog = inputdialog:new("input", rect{0, 0, 50, 10}) dialog:text():text_set("请输入您的姓名:") dialog:background_set(self:background()) dialog:frame():background_set("cyan") -- 添加按钮 dialog:button_add("yes", "< 是 >", function (v) local input = dialog:textedit():text() print("输入值:", input) dialog:quit() end) dialog:button_add("no", "< 否 >", function (v) dialog:quit() end) -- 显示对话框 dialog:show(false) -- 初始隐藏 -- 插入到应用程序 self:insert(dialog, {centerx = true, centery = true}) end function app:on_resize() self:dialog_input():bounds_set(rect{0, 0, 50, 10}) self:center(self:dialog_input(), {centerx = true, centery = true}) application.on_resize(self) end app:run() ``` --- --- url: /api/scripts/extension-modules/core/ui/label.md --- # core.ui.label This module provides a text label view for displaying text in the terminal UI. ::: tip TIP To use this module, you need to import it first: `import("core.ui.label")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `label` module extends `view` and provides a simple text display component with: * Text content management * Text attribute (color, bold, etc.) support * Automatic text wrapping based on width * UTF-8 character width calculation ## label:new * Create a new label instance #### Function Prototype ::: tip API ```lua label:new(name: , bounds: , text: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Label name string | | bounds | Required. Label bounds rectangle | | text | Required. Text content to display | #### Return Value | Type | Description | |------|-------------| | label | Returns a label instance | #### Usage Create a label with text: ```lua import("core.ui.label") import("core.ui.rect") local lbl = label:new("hello", rect{1, 1, 20, 1}, "Hello, World!") ``` ## label:text * Get the current text content #### Function Prototype ::: tip API ```lua label:text() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the text content | #### Usage Get the text content of a label: ```lua local text = lbl:text() print(text) -- Output: Hello, World! ``` ## label:text\_set * Set the text content #### Function Prototype ::: tip API ```lua label:text_set(text: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | text | Required. Text content to display | #### Return Value | Type | Description | |------|-------------| | label | Returns the label instance (for method chaining) | #### Usage Set or update the text content: ```lua lbl:text_set("New Text") lbl:text_set("Line 1\nLine 2") -- Multi-line text ``` ## label:textattr * Get the text attribute string #### Function Prototype ::: tip API ```lua label:textattr() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the text attribute string (e.g., "red bold") | #### Usage Get the current text attribute: ```lua local attr = lbl:textattr() print(attr) -- Output: black ``` ## label:textattr\_set * Set the text attribute (color, style) #### Function Prototype ::: tip API ```lua label:textattr_set(attr: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | attr | Required. Text attribute string (e.g., "red bold", "yellow onblue") | #### Return Value | Type | Description | |------|-------------| | label | Returns the label instance (for method chaining) | #### Usage Set text color and style: ```lua lbl:textattr_set("red") -- Red text lbl:textattr_set("yellow bold") -- Yellow bold text lbl:textattr_set("cyan onblue") -- Cyan text on blue background lbl:textattr_set("green bold onblack") -- Green bold text on black background ``` ## label:textattr\_val * Get the compiled text attribute value #### Function Prototype ::: tip API ```lua label:textattr_val() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns the numeric attribute value (for curses) | #### Usage This is typically used internally by the drawing system. The returned value combines the text attribute with the view's background color. ## label:splitext * Split text into lines based on width #### Function Prototype ::: tip API ```lua label:splitext(text: , width?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | text | Required. Text to split | | width | Optional. Maximum line width (defaults to label width) | #### Return Value | Type | Description | |------|-------------| | table | Returns array of text lines | #### Usage Split text into multiple lines: ```lua local lines = lbl:splitext("This is a long text that needs to be wrapped", 10) -- Returns: {"This is a ", "long text ", "that needs ", "to be ", "wrapped"} ``` ## label:on\_draw * Draw the label view #### Function Prototype ::: tip API ```lua label:on_draw(transparent: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | transparent | Optional. Draw with transparency | #### Return Value No return value #### Usage This is automatically called by the UI framework when the label needs to be drawn. --- --- url: /zh/api/scripts/extension-modules/core/ui/label.md --- # core.ui.label 此模块提供用于在终端 UI 中显示文本的标签视图。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.label")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `label` 模块继承自 `view`,提供简单的文本显示组件,包含: * 文本内容管理 * 文本属性(颜色、粗体等)支持 * 基于宽度的自动文本换行 * UTF-8 字符宽度计算 ## label:new * 创建新的标签实例 #### 函数原型 ::: tip API ```lua label:new(name: , bounds: , text: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。标签名称字符串 | | bounds | 必需。标签边界矩形 | | text | 必需。要显示的文本内容 | #### 返回值说明 | 类型 | 描述 | |------|------| | label | 返回标签实例 | #### 用法说明 创建带文本的标签: ```lua import("core.ui.label") import("core.ui.rect") local lbl = label:new("hello", rect{1, 1, 20, 1}, "你好,世界!") ``` ## label:text * 获取当前文本内容 #### 函数原型 ::: tip API ```lua label:text() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回文本内容 | #### 用法说明 获取标签的文本内容: ```lua local text = lbl:text() print(text) -- 输出: 你好,世界! ``` ## label:text\_set * 设置文本内容 #### 函数原型 ::: tip API ```lua label:text_set(text: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | text | 必需。要显示的文本内容 | #### 返回值说明 | 类型 | 描述 | |------|------| | label | 返回标签实例(用于链式调用) | #### 用法说明 设置或更新文本内容: ```lua lbl:text_set("新文本") lbl:text_set("第一行\n第二行") -- 多行文本 ``` ## label:textattr * 获取文本属性字符串 #### 函数原型 ::: tip API ```lua label:textattr() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回文本属性字符串(例如:"red bold") | #### 用法说明 获取当前文本属性: ```lua local attr = lbl:textattr() print(attr) -- 输出: black ``` ## label:textattr\_set * 设置文本属性(颜色、样式) #### 函数原型 ::: tip API ```lua label:textattr_set(attr: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | attr | 必需。文本属性字符串(例如:"red bold"、"yellow onblue") | #### 返回值说明 | 类型 | 描述 | |------|------| | label | 返回标签实例(用于链式调用) | #### 用法说明 设置文本颜色和样式: ```lua lbl:textattr_set("red") -- 红色文本 lbl:textattr_set("yellow bold") -- 黄色粗体文本 lbl:textattr_set("cyan onblue") -- 蓝色背景上的青色文本 lbl:textattr_set("green bold onblack") -- 黑色背景上的绿色粗体文本 ``` ## label:textattr\_val * 获取编译后的文本属性值 #### 函数原型 ::: tip API ```lua label:textattr_val() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回数字属性值(用于 curses) | #### 用法说明 这通常由绘制系统内部使用。返回的值将文本属性与视图的背景颜色组合在一起。 ## label:splitext * 根据宽度将文本拆分为行 #### 函数原型 ::: tip API ```lua label:splitext(text: , width?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | text | 必需。要拆分的文本 | | width | 可选。最大行宽(默认为标签宽度) | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回文本行数组 | #### 用法说明 将文本拆分为多行: ```lua local lines = lbl:splitext("这是一个需要换行的长文本", 10) -- 返回: {"这是一个需要", "换行的长文本"} ``` ## label:on\_draw * 绘制标签视图 #### 函数原型 ::: tip API ```lua label:on_draw(transparent: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | transparent | 可选。使用透明度绘制 | #### 返回值说明 无返回值 #### 用法说明 当需要绘制标签时,这由 UI 框架自动调用。以下是一个完整示例: ```lua import("core.ui.label") import("core.ui.rect") import("core.ui.application") import("core.ui.window") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "标签演示") -- 创建不同样式的标签 local lbl1 = label:new("lbl1", rect{2, 2, 30, 1}, "你好,世界!") lbl1:textattr_set("green") local lbl2 = label:new("lbl2", rect{2, 4, 30, 1}, "这是粗体文本!") lbl2:textattr_set("yellow bold") local lbl3 = label:new("lbl3", rect{2, 6, 30, 1}, "彩色背景!") lbl3:textattr_set("cyan onblack") local lbl4 = label:new("lbl4", rect{2, 8, 30, 3}, "多行文本\n第二行\n第三行") lbl4:textattr_set("red") -- 将标签添加到窗口面板 local panel = win:panel() panel:insert(lbl1) panel:insert(lbl2) panel:insert(lbl3) panel:insert(lbl4) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/mconfdialog.md --- # core.ui.mconfdialog This module provides a menu-driven configuration dialog for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.mconfdialog")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `mconfdialog` module extends `boxdialog` and provides a comprehensive menu-based configuration interface. It allows users to configure settings through a hierarchical menu, with support for boolean, number, string, choice, and menu configurations. It also includes features like help dialogs, search functionality, and scrollbar support for long menus. ## mconfdialog:new * Create a new menu configuration dialog instance #### Function Prototype ::: tip API ```lua mconfdialog:new(name: , bounds: , title: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Dialog name string | | bounds | Required. Dialog bounds rectangle | | title | Required. Dialog title string | #### Return Value | Type | Description | |------|-------------| | mconfdialog | Returns a menu configuration dialog instance | #### Usage Create a menu configuration dialog: ```lua import("core.ui.mconfdialog") import("core.ui.rect") local dialog = mconfdialog:new("config", rect{1, 1, 80, 25}, "Configuration") ``` ## mconfdialog:load * Load configuration items into the dialog #### Function Prototype ::: tip API ```lua mconfdialog:load(configs:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | configs | Required. Array of configuration items (created with `menuconf.*` functions) | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` on success | #### Usage Load configurations into the dialog: ```lua local configs = { menuconf.boolean{description = "Enable debug"}, menuconf.string{value = "x86_64", description = "Target architecture"}, menuconf.number{value = 10, default = 10, description = "Thread count"}, menuconf.choice{ value = 2, values = {"gcc", "clang", "msvc"}, description = "Compiler" } } dialog:load(configs) ``` ## mconfdialog:configs * Get all loaded configurations #### Function Prototype ::: tip API ```lua mconfdialog:configs() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | table | Returns array of configuration items | #### Usage Access loaded configurations: ```lua local configs = dialog:configs() for _, config in ipairs(configs) do print("Config:", config:prompt()) print("Value:", config.value) end ``` ## mconfdialog:menuconf * Get the menu config component #### Function Prototype ::: tip API ```lua mconfdialog:menuconf() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | menuconf | Returns the menu configuration instance | #### Usage Access the underlying menuconf component: ```lua local menu = dialog:menuconf() ``` ## mconfdialog:show\_help * Show help dialog for the current configuration item #### Function Prototype ::: tip API ```lua mconfdialog:show_help() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage Display help information for the currently selected configuration item: ```lua -- This is typically triggered by pressing '?' key dialog:show_help() ``` ## mconfdialog:show\_search * Show search dialog to search configurations #### Function Prototype ::: tip API ```lua mconfdialog:show_search() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage Show search dialog to find configuration items: ```lua -- This is typically triggered by pressing '/' key dialog:show_search() ``` ## mconfdialog:show\_exit * Show exit confirmation dialog #### Function Prototype ::: tip API ```lua mconfdialog:show_exit(message: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | message | Required. Confirmation message to display | #### Return Value No return value #### Usage Show exit confirmation dialog: ```lua dialog:show_exit("Did you wish to save your new configuration?") ``` ## mconfdialog:on\_event * Handle keyboard events #### Function Prototype ::: tip API ```lua mconfdialog:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if event was handled, `false` otherwise | #### Usage Handle keyboard events for configuration navigation: ```lua function dialog:on_event(e) if e.type == event.ev_keyboard then if e.key_name == "?" then self:show_help() return true elseif e.key_name == "/" then self:show_search() return true end end return mconfdialog.on_event(self, e) end ``` The following keys are supported: * `Up/Down`: Navigate menu items * `Space`: Toggle boolean values * `Y`: Enable boolean * `N`: Disable boolean * `Esc/Back`: Go back * `?`: Show help * `/`: Search --- --- url: /zh/api/scripts/extension-modules/core/ui/mconfdialog.md --- # core.ui.mconfdialog 此模块为终端 UI 系统提供基于菜单的配置对话框。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.mconfdialog")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `mconfdialog` 模块继承自 `boxdialog`,提供完整的基于菜单的配置界面。它允许用户通过分层菜单配置设置,支持 boolean、number、string、choice 和 menu 配置类型。它还包含帮助对话框、搜索功能和滚动条支持等特性。 ## mconfdialog:new * 创建新的菜单配置对话框实例 #### 函数原型 ::: tip API ```lua mconfdialog:new(name: , bounds: , title: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。对话框名称字符串 | | bounds | 必需。对话框边界矩形 | | title | 必需。对话框标题字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | mconfdialog | 返回菜单配置对话框实例 | #### 用法说明 创建菜单配置对话框: ```lua import("core.ui.mconfdialog") import("core.ui.rect") local dialog = mconfdialog:new("config", rect{1, 1, 80, 25}, "配置") ``` ## mconfdialog:load * 将配置项加载到对话框中 #### 函数原型 ::: tip API ```lua mconfdialog:load(configs:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | configs | 必需。配置项数组(使用 `menuconf.*` 函数创建) | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 成功时返回 `true` | #### 用法说明 将配置加载到对话框: ```lua local configs = { menuconf.boolean{description = "启用调试"}, menuconf.string{value = "x86_64", description = "目标架构"}, menuconf.number{value = 10, default = 10, description = "线程数"}, menuconf.choice{ value = 2, values = {"gcc", "clang", "msvc"}, description = "编译器" } } dialog:load(configs) ``` ## mconfdialog:configs * 获取所有已加载的配置 #### 函数原型 ::: tip API ```lua mconfdialog:configs() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回配置项数组 | #### 用法说明 访问已加载的配置: ```lua local configs = dialog:configs() for _, config in ipairs(configs) do print("配置:", config:prompt()) print("值:", config.value) end ``` ## mconfdialog:menuconf * 获取菜单配置组件 #### 函数原型 ::: tip API ```lua mconfdialog:menuconf() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | menuconf | 返回菜单配置实例 | #### 用法说明 访问底层 menuconf 组件: ```lua local menu = dialog:menuconf() ``` ## mconfdialog:show\_help * 显示当前配置项的帮助对话框 #### 函数原型 ::: tip API ```lua mconfdialog:show_help() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 显示当前选中配置项的帮助信息: ```lua -- 这通常通过按 '?' 键触发 dialog:show_help() ``` ## mconfdialog:show\_search * 显示搜索对话框以搜索配置 #### 函数原型 ::: tip API ```lua mconfdialog:show_search() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 显示搜索对话框以查找配置项: ```lua -- 这通常通过按 '/' 键触发 dialog:show_search() ``` ## mconfdialog:show\_exit * 显示退出确认对话框 #### 函数原型 ::: tip API ```lua mconfdialog:show_exit(message: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | message | 必需。要显示的确认消息 | #### 返回值说明 无返回值 #### 用法说明 显示退出确认对话框: ```lua dialog:show_exit("您是否希望保存新的配置?") ``` ## mconfdialog:on\_event * 处理键盘事件 #### 函数原型 ::: tip API ```lua mconfdialog:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件已处理返回 `true`,否则返回 `false` | #### 用法说明 处理配置导航的键盘事件: ```lua function dialog:on_event(e) if e.type == event.ev_keyboard then if e.key_name == "?" then self:show_help() return true elseif e.key_name == "/" then self:show_search() return true end end return mconfdialog.on_event(self, e) end ``` 支持以下按键: * `Up/Down`:导航菜单项 * `Space`:切换布尔值 * `Y`:启用布尔值 * `N`:禁用布尔值 * `Esc/Back`:返回 * `?`:显示帮助 * `/`:搜索 以下是一个完整的菜单配置对话框示例: ```lua import("core.ui.mconfdialog") import("core.ui.menuconf") import("core.ui.rect") import("core.ui.action") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建配置项 local configs_sub = {} table.insert(configs_sub, menuconf.boolean{description = "启用调试"}) table.insert(configs_sub, menuconf.string{value = "x86_64", description = "架构"}) table.insert(configs_sub, menuconf.number{value = 4, default = 4, description = "线程数"}) local configs = {} table.insert(configs, menuconf.boolean{description = "启用优化"}) table.insert(configs, menuconf.string{value = "Release", description = "构建模式"}) table.insert(configs, menuconf.choice{ value = 2, values = {"Debug", "Release", "MinSizeRel", "RelWithDebInfo"}, description = "配置" }) table.insert(configs, menuconf.menu{description = "高级选项", configs = configs_sub}) -- 创建菜单配置对话框 local dialog = mconfdialog:new("config", rect{1, 1, self:width() - 1, self:height() - 1}, "构建配置") dialog:load(configs) -- 处理退出 dialog:action_set(action.ac_on_exit, function (v) self:quit() end) -- 处理保存 dialog:action_set(action.ac_on_save, function (v) -- 处理保存的配置 local configs = dialog:configs() for _, config in ipairs(configs) do print("已保存:", config:prompt(), "=", config.value) end dialog:quit() end) self:insert(dialog) self._dialog = dialog end function demo:on_resize() self._dialog:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/menubar.md --- # core.ui.menubar This module provides a menu bar component for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.menubar")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `menubar` module extends `panel` and provides menu bar display functionality. ## menubar:new * Create a new menu bar instance #### Function Prototype ::: tip API ```lua menubar:new(name: , bounds: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Menu bar name string | | bounds | Required. Menu bar bounds rectangle | #### Return Value | Type | Description | |------|-------------| | menubar | Returns a menu bar instance | #### Usage Create a menu bar: ```lua import("core.ui.menubar") import("core.ui.rect") local menubar = menubar:new("menubar", rect{1, 1, 80, 1}) ``` ## menubar:title * Get the title label #### Function Prototype ::: tip API ```lua menubar:title() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | label | Returns the title label instance | #### Usage Access and customize the title label: ```lua local title = menubar:title() title:text_set("My Application") title:textattr_set("red bold") ``` Here is a complete menu bar usage example: ```lua import("core.ui.menubar") import("core.ui.window") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create menu bar local menubar = menubar:new("menubar", rect{1, 1, self:width() - 1, 1}) menubar:title():text_set("Xmake Demo Application") menubar:title():textattr_set("red bold") -- Create main window local win = window:new("main", rect{1, 2, self:width() - 1, self:height() - 2}, "Main Window") -- Add to application self:insert(menubar) self:insert(win) self._menubar = menubar self._win = win end function demo:on_resize() self._menubar:bounds_set(rect{1, 1, self:width() - 1, 1}) self._win:bounds_set(rect{1, 2, self:width() - 1, self:height() - 2}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/menubar.md --- # core.ui.menubar 此模块为终端 UI 系统提供菜单栏组件。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.menubar")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `menubar` 模块继承自 `panel`,提供菜单栏显示。 ## menubar:new * 创建新的菜单栏实例 #### 函数原型 ::: tip API ```lua menubar:new(name: , bounds: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。菜单栏名称字符串 | | bounds | 必需。菜单栏边界矩形 | #### 返回值说明 | 类型 | 描述 | |------|------| | menubar | 返回菜单栏实例 | #### 用法说明 创建菜单栏: ```lua import("core.ui.menubar") import("core.ui.rect") local menubar = menubar:new("menubar", rect{1, 1, 80, 1}) ``` ## menubar:title * 获取标题标签 #### 函数原型 ::: tip API ```lua menubar:title() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | label | 返回标题标签实例 | #### 用法说明 访问并自定义标题标签: ```lua local title = menubar:title() title:text_set("My Application") title:textattr_set("red bold") ``` 以下是一个完整的菜单栏使用示例: ```lua import("core.ui.menubar") import("core.ui.window") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建菜单栏 local menubar = menubar:new("menubar", rect{1, 1, self:width() - 1, 1}) menubar:title():text_set("Xmake Demo Application") menubar:title():textattr_set("red bold") -- 创建主窗口 local win = window:new("main", rect{1, 2, self:width() - 1, self:height() - 2}, "主窗口") -- 添加到应用程序 self:insert(menubar) self:insert(win) self._menubar = menubar self._win = win end function demo:on_resize() self._menubar:bounds_set(rect{1, 1, self:width() - 1, 1}) self._win:bounds_set(rect{1, 2, self:width() - 1, self:height() - 2}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/scrollbar.md --- # core.ui.scrollbar This module provides a scrollbar component for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.scrollbar")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `scrollbar` module extends `view` and provides horizontal and vertical scrollbar functionality. ## scrollbar:new * Create a new scrollbar instance #### Function Prototype ::: tip API ```lua scrollbar:new(name: , bounds: , vertical?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Scrollbar name string | | bounds | Required. Scrollbar bounds rectangle | | vertical | Optional. Whether it's a vertical scrollbar, defaults to `true` | #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns a scrollbar instance | #### Usage Create vertical and horizontal scrollbars: ```lua import("core.ui.scrollbar") import("core.ui.rect") local vscrollbar = scrollbar:new("vbar", rect{50, 1, 1, 20}, true) -- Vertical scrollbar local hscrollbar = scrollbar:new("hbar", rect{1, 20, 50, 1}, false) -- Horizontal scrollbar ``` ## scrollbar:progress * Get current scroll progress #### Function Prototype ::: tip API ```lua scrollbar:progress() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns progress value, range 0.0 to 1.0 | #### Usage Get current scroll progress: ```lua local progress = scrollbar:progress() print("Scroll progress:", progress) ``` ## scrollbar:progress\_set * Set scroll progress #### Function Prototype ::: tip API ```lua scrollbar:progress_set(progress: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | progress | Required. Progress value, range 0.0 to 1.0 | #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns the scrollbar instance (for chaining) | #### Usage Set scroll progress: ```lua scrollbar:progress_set(0.5) -- Set to middle position scrollbar:progress_set(0) -- Set to top scrollbar:progress_set(1) -- Set to bottom ``` ## scrollbar:stepwidth * Get step width #### Function Prototype ::: tip API ```lua scrollbar:stepwidth() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns step width, range 0.0 to 1.0 | #### Usage Get step width: ```lua local stepwidth = scrollbar:stepwidth() ``` ## scrollbar:stepwidth\_set * Set step width #### Function Prototype ::: tip API ```lua scrollbar:stepwidth_set(stepwidth: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | stepwidth | Required. Step width, range 0.0 to 1.0 | #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns the scrollbar instance (for chaining) | #### Usage Set step width: ```lua scrollbar:stepwidth_set(0.1) -- Scroll 10% each time ``` ## scrollbar:vertical * Check if it's a vertical scrollbar #### Function Prototype ::: tip API ```lua scrollbar:vertical() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if vertical, otherwise returns `false` | #### Usage Check scrollbar direction: ```lua if scrollbar:vertical() then print("This is a vertical scrollbar") else print("This is a horizontal scrollbar") end ``` ## scrollbar:vertical\_set * Set scrollbar direction #### Function Prototype ::: tip API ```lua scrollbar:vertical_set(vertical: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | vertical | Required. Set direction, `true` for vertical, `false` for horizontal | #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns the scrollbar instance (for chaining) | #### Usage Set scrollbar direction: ```lua scrollbar:vertical_set(true) -- Set to vertical scrollbar:vertical_set(false) -- Set to horizontal ``` ## scrollbar:char * Get scrollbar character #### Function Prototype ::: tip API ```lua scrollbar:char() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the character displayed in the scrollbar | #### Usage Get scrollbar character: ```lua local char = scrollbar:char() ``` ## scrollbar:char\_set * Set scrollbar character #### Function Prototype ::: tip API ```lua scrollbar:char_set(char: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | char | Required. Character displayed in the scrollbar | #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns the scrollbar instance (for chaining) | #### Usage Set scrollbar character: ```lua scrollbar:char_set('#') -- Use # character scrollbar:char_set('\u2588') -- Use block character ``` ## scrollbar:charattr * Get scrollbar character attribute #### Function Prototype ::: tip API ```lua scrollbar:charattr() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns character attribute string | #### Usage Get character attribute: ```lua local attr = scrollbar:charattr() ``` ## scrollbar:charattr\_set * Set scrollbar character attribute #### Function Prototype ::: tip API ```lua scrollbar:charattr_set(attr: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | attr | Required. Character attribute string | #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns the scrollbar instance (for chaining) | #### Usage Set character attribute: ```lua scrollbar:charattr_set("yellow on blue bold") -- Yellow bold text on blue background ``` ## scrollbar:scroll * Scroll the scrollbar #### Function Prototype ::: tip API ```lua scrollbar:scroll(steps?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | steps | Optional. Number of steps, positive for down/right, negative for up/left, defaults to 1 | #### Return Value No return value #### Usage Scroll the scrollbar: ```lua scrollbar:scroll(1) -- Scroll down/right one step scrollbar:scroll(-1) -- Scroll up/left one step scrollbar:scroll(5) -- Scroll 5 steps ``` ## scrollbar:on\_event * Handle keyboard events #### Function Prototype ::: tip API ```lua scrollbar:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if the event was handled, otherwise returns `false` | #### Usage The scrollbar automatically handles the following keyboard events: * Vertical scrollbar: `Up` (scroll up), `Down` (scroll down) * Horizontal scrollbar: `Left` (scroll left), `Right` (scroll right) Here is a complete scrollbar usage example: ```lua import("core.ui.scrollbar") import("core.ui.rect") import("core.ui.label") import("core.ui.window") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create window local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "Scrollbar Demo") -- Create scrollbar local scrollbar = scrollbar:new("scroll", rect{self:width() - 2, 1, 1, self:height() - 2}, true) scrollbar:char_set('#') scrollbar:charattr_set("yellow on blue") scrollbar:stepwidth_set(0.1) -- Create label to display current progress local label = label:new("progress", rect{1, 1, 30, 1}, "Progress: 0%") -- Set scroll event scrollbar:action_set(action.ac_on_scrolled, function (v, progress) local text = string.format("Progress: %.1f%%", progress * 100) label:text_set(text) end) -- Add components to window panel local panel = win:panel() panel:insert(label) panel:insert(scrollbar) self:insert(win) self._win = win self._scrollbar = scrollbar end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) self._scrollbar:bounds_set(rect{self:width() - 2, 1, 1, self:height() - 2}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/scrollbar.md --- # core.ui.scrollbar 此模块提供用于终端 UI 系统的滚动条组件。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.scrollbar")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `scrollbar` 模块继承自 `view`,提供水平和垂直滚动条功能。 ## scrollbar:new * 创建新的滚动条实例 #### 函数原型 ::: tip API ```lua scrollbar:new(name: , bounds: , vertical?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。滚动条名称字符串 | | bounds | 必需。滚动条边界矩形 | | vertical | 可选。是否为垂直滚动条,默认为 `true` | #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例 | #### 用法说明 创建垂直和水平滚动条: ```lua import("core.ui.scrollbar") import("core.ui.rect") local vscrollbar = scrollbar:new("vbar", rect{50, 1, 1, 20}, true) -- 垂直滚动条 local hscrollbar = scrollbar:new("hbar", rect{1, 20, 50, 1}, false) -- 水平滚动条 ``` ## scrollbar:progress * 获取当前滚动进度 #### 函数原型 ::: tip API ```lua scrollbar:progress() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回进度值,范围为 0.0 到 1.0 | #### 用法说明 获取当前滚动进度: ```lua local progress = scrollbar:progress() print("滚动进度:", progress) ``` ## scrollbar:progress\_set * 设置滚动进度 #### 函数原型 ::: tip API ```lua scrollbar:progress_set(progress: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | progress | 必需。进度值,范围为 0.0 到 1.0 | #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例(用于链式调用) | #### 用法说明 设置滚动进度: ```lua scrollbar:progress_set(0.5) -- 设置到中间位置 scrollbar:progress_set(0) -- 设置到顶部 scrollbar:progress_set(1) -- 设置到底部 ``` ## scrollbar:stepwidth * 获取步进宽度 #### 函数原型 ::: tip API ```lua scrollbar:stepwidth() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回步进宽度,范围为 0.0 到 1.0 | #### 用法说明 获取步进宽度: ```lua local stepwidth = scrollbar:stepwidth() ``` ## scrollbar:stepwidth\_set * 设置步进宽度 #### 函数原型 ::: tip API ```lua scrollbar:stepwidth_set(stepwidth: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | stepwidth | 必需。步进宽度,范围为 0.0 到 1.0 | #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例(用于链式调用) | #### 用法说明 设置步进宽度: ```lua scrollbar:stepwidth_set(0.1) -- 每次滚动 10% ``` ## scrollbar:vertical * 检查是否为垂直滚动条 #### 函数原型 ::: tip API ```lua scrollbar:vertical() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果是垂直滚动条返回 `true`,否则返回 `false` | #### 用法说明 检查滚动条方向: ```lua if scrollbar:vertical() then print("这是垂直滚动条") else print("这是水平滚动条") end ``` ## scrollbar:vertical\_set * 设置滚动条方向 #### 函数原型 ::: tip API ```lua scrollbar:vertical_set(vertical: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | vertical | 必需。设置方向,`true` 为垂直,`false` 为水平 | #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例(用于链式调用) | #### 用法说明 设置滚动条方向: ```lua scrollbar:vertical_set(true) -- 设置为垂直 scrollbar:vertical_set(false) -- 设置为水平 ``` ## scrollbar:char * 获取滚动条字符 #### 函数原型 ::: tip API ```lua scrollbar:char() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回滚动条显示的字符 | #### 用法说明 获取滚动条字符: ```lua local char = scrollbar:char() ``` ## scrollbar:char\_set * 设置滚动条字符 #### 函数原型 ::: tip API ```lua scrollbar:char_set(char: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | char | 必需。滚动条显示的字符 | #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例(用于链式调用) | #### 用法说明 设置滚动条字符: ```lua scrollbar:char_set('#') -- 使用 # 字符 scrollbar:char_set('\u2588') -- 使用方块字符 ``` ## scrollbar:charattr * 获取滚动条字符属性 #### 函数原型 ::: tip API ```lua scrollbar:charattr() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回字符属性字符串 | #### 用法说明 获取字符属性: ```lua local attr = scrollbar:charattr() ``` ## scrollbar:charattr\_set * 设置滚动条字符属性 #### 函数原型 ::: tip API ```lua scrollbar:charattr_set(attr: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | attr | 必需。字符属性字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例(用于链式调用) | #### 用法说明 设置字符属性: ```lua scrollbar:charattr_set("yellow onblue bold") -- 黄色粗体,蓝色背景 ``` ## scrollbar:scroll * 滚动滚动条 #### 函数原型 ::: tip API ```lua scrollbar:scroll(steps?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | steps | 可选。步数,正数向下/向右,负数向上/向左,默认为 1 | #### 返回值说明 无返回值 #### 用法说明 滚动滚动条: ```lua scrollbar:scroll(1) -- 向下/向右滚动一步 scrollbar:scroll(-1) -- 向上/向左滚动一步 scrollbar:scroll(5) -- 滚动 5 步 ``` ## scrollbar:on\_event * 处理键盘事件 #### 函数原型 ::: tip API ```lua scrollbar:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件已处理返回 `true`,否则返回 `false` | #### 用法说明 滚动条自动处理以下键盘事件: * 垂直滚动条:`Up`(向上)、`Down`(向下) * 水平滚动条:`Left`(向左)、`Right`(向右) 以下是一个完整的滚动条使用示例: ```lua import("core.ui.scrollbar") import("core.ui.rect") import("core.ui.label") import("core.ui.window") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "滚动条演示") -- 创建滚动条 local scrollbar = scrollbar:new("scroll", rect{self:width() - 2, 1, 1, self:height() - 2}, true) scrollbar:char_set('#') scrollbar:charattr_set("yellow on blue") scrollbar:stepwidth_set(0.1) -- 创建标签显示当前进度 local label = label:new("progress", rect{1, 1, 30, 1}, "进度: 0%") -- 设置滚动事件 scrollbar:action_set(action.ac_on_scrolled, function (v, progress) local text = string.format("进度: %.1f%%", progress * 100) label:text_set(text) end) -- 将组件添加到窗口面板 local panel = win:panel() panel:insert(label) panel:insert(scrollbar) self:insert(win) self._win = win self._scrollbar = scrollbar end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) self._scrollbar:bounds_set(rect{self:width() - 2, 1, 1, self:height() - 2}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/statusbar.md --- # core.ui.statusbar This module provides a status bar component for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.statusbar")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `statusbar` module extends `panel` and provides status bar display functionality. ## statusbar:new * Create a new status bar instance #### Function Prototype ::: tip API ```lua statusbar:new(name: , bounds: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Status bar name string | | bounds | Required. Status bar bounds rectangle | #### Return Value | Type | Description | |------|-------------| | statusbar | Returns a status bar instance | #### Usage Create a status bar: ```lua import("core.ui.statusbar") import("core.ui.rect") local statusbar = statusbar:new("status", rect{1, 25, 80, 1}) ``` ## statusbar:info * Get the info label #### Function Prototype ::: tip API ```lua statusbar:info() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | label | Returns the info label instance | #### Usage Access and customize the info label: ```lua local info = statusbar:info() info:text_set("Ready") info:textattr_set("blue bold") ``` Here is a complete status bar usage example: ```lua import("core.ui.statusbar") import("core.ui.window") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create status bar local statusbar = statusbar:new("status", rect{1, self:height() - 1, self:width() - 1, 1}) statusbar:info():text_set("Ready") statusbar:info():textattr_set("blue bold") -- Create main window local win = window:new("main", rect{1, 2, self:width() - 1, self:height() - 3}, "Main Window") -- Add to application self:insert(win) self:insert(statusbar) self._statusbar = statusbar self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 2, self:width() - 1, self:height() - 3}) self._statusbar:bounds_set(rect{1, self:height() - 1, self:width() - 1, 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/statusbar.md --- # core.ui.statusbar 此模块为终端 UI 系统提供状态栏组件。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.statusbar")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `statusbar` 模块继承自 `panel`,提供状态栏显示功能。 ## statusbar:new * 创建新的状态栏实例 #### 函数原型 ::: tip API ```lua statusbar:new(name: , bounds: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。状态栏名称字符串 | | bounds | 必需。状态栏边界矩形 | #### 返回值说明 | 类型 | 描述 | |------|------| | statusbar | 返回状态栏实例 | #### 用法说明 创建状态栏: ```lua import("core.ui.statusbar") import("core.ui.rect") local statusbar = statusbar:new("status", rect{1, 25, 80, 1}) ``` ## statusbar:info * 获取信息标签 #### 函数原型 ::: tip API ```lua statusbar:info() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | label | 返回信息标签实例 | #### 用法说明 访问并自定义信息标签: ```lua local info = statusbar:info() info:text_set("就绪") info:textattr_set("blue bold") ``` 以下是一个完整的状态栏使用示例: ```lua import("core.ui.statusbar") import("core.ui.window") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建状态栏 local statusbar = statusbar:new("status", rect{1, self:height() - 1, self:width() - 1, 1}) statusbar:info():text_set("就绪") statusbar:info():textattr_set("blue bold") -- 创建主窗口 local win = window:new("main", rect{1, 2, self:width() - 1, self:height() - 3}, "主窗口") -- 添加到应用程序 self:insert(win) self:insert(statusbar) self._statusbar = statusbar self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 2, self:width() - 1, self:height() - 3}) self._statusbar:bounds_set(rect{1, self:height() - 1, self:width() - 1, 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/textdialog.md --- # core.ui.textdialog This module provides a dialog with a text area for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.textdialog")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `textdialog` module extends `dialog` and provides a dialog with a text area. It contains a text area (textarea) and optional scrollbar support, and serves as the base for dialogs like `inputdialog` and `boxdialog`. ## textdialog:new * Create a new text dialog instance #### Function Prototype ::: tip API ```lua textdialog:new(name: , bounds: , title?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Dialog name string | | bounds | Required. Dialog bounds rectangle | | title | Optional. Dialog title string | #### Return Value | Type | Description | |------|-------------| | textdialog | Returns a text dialog instance | #### Usage Create a text dialog: ```lua import("core.ui.textdialog") import("core.ui.rect") local dialog = textdialog:new("text", rect{10, 5, 50, 20}, "Text Dialog") ``` ## textdialog:text * Get the text area component #### Function Prototype ::: tip API ```lua textdialog:text() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | textarea | Returns the text area instance | #### Usage Access the text area to set or get text content: ```lua local textarea = dialog:text() textarea:text_set("This is text content") ``` ## textdialog:scrollbar * Get the scrollbar component #### Function Prototype ::: tip API ```lua textdialog:scrollbar() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | scrollbar | Returns the scrollbar instance | #### Usage Access the scrollbar component: ```lua local scrollbar = dialog:scrollbar() scrollbar:show(true) ``` ## textdialog:option\_set * Set dialog options #### Function Prototype ::: tip API ```lua textdialog:option_set(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Option name, supports: `"scrollable"` | | value | Required. Option value | #### Return Value No return value #### Usage Enable or disable scrollbar: ```lua -- Enable scrollbar dialog:option_set("scrollable", true) -- Disable scrollbar dialog:option_set("scrollable", false) ``` ## textdialog:on\_event * Handle dialog events #### Function Prototype ::: tip API ```lua textdialog:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if the event was handled, otherwise returns `false` | #### Usage The dialog automatically passes keyboard events to the text area to support scrolling. Here is a complete text dialog example: ```lua import("core.ui.textdialog") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create text dialog local dialog = textdialog:new("text", rect{10, 5, 60, 20}, "Text Dialog") -- Set text content dialog:text():text_set("This is a text dialog example.\nYou can display multi-line text content.\nWhen text content exceeds the display area, you can enable scrolling.") -- Enable scrollbar dialog:option_set("scrollable", true) -- Add buttons dialog:button_add("ok", "< OK >", function (v) self:quit() end) self:insert(dialog) self._dialog = dialog end function demo:on_resize() if self._dialog then self._dialog:bounds_set(rect{10, 5, self:width() - 20, self:height() - 10}) end application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/textdialog.md --- # core.ui.textdialog 此模块为终端 UI 系统提供带文本区域的对话框。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.textdialog")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `textdialog` 模块继承自 `dialog`,提供带文本区域的对话框。它包含一个文本区域(textarea)和可选的滚动条支持,是 `inputdialog` 和 `boxdialog` 等对话框的基础。 ## textdialog:new * 创建新的文本对话框实例 #### 函数原型 ::: tip API ```lua textdialog:new(name: , bounds: , title?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。对话框名称字符串 | | bounds | 必需。对话框边界矩形 | | title | 可选。对话框标题字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | textdialog | 返回文本对话框实例 | #### 用法说明 创建文本对话框: ```lua import("core.ui.textdialog") import("core.ui.rect") local dialog = textdialog:new("text", rect{10, 5, 50, 20}, "文本对话框") ``` ## textdialog:text * 获取文本区域组件 #### 函数原型 ::: tip API ```lua textdialog:text() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | textarea | 返回文本区域实例 | #### 用法说明 访问文本区域以设置或获取文本内容: ```lua local textarea = dialog:text() textarea:text_set("这是文本内容") ``` ## textdialog:scrollbar * 获取滚动条组件 #### 函数原型 ::: tip API ```lua textdialog:scrollbar() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | scrollbar | 返回滚动条实例 | #### 用法说明 访问滚动条组件: ```lua local scrollbar = dialog:scrollbar() scrollbar:show(true) ``` ## textdialog:option\_set * 设置对话框选项 #### 函数原型 ::: tip API ```lua textdialog:option_set(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。选项名称,支持:`"scrollable"` | | value | 必需。选项值 | #### 返回值说明 无返回值 #### 用法说明 启用或禁用滚动条: ```lua -- 启用滚动条 dialog:option_set("scrollable", true) -- 禁用滚动条 dialog:option_set("scrollable", false) ``` ## textdialog:on\_event * 处理对话框事件 #### 函数原型 ::: tip API ```lua textdialog:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件已处理返回 `true`,否则返回 `false` | #### 用法说明 对话框会自动将键盘事件传递给文本区域以支持滚动。 以下是一个完整的文本对话框示例: ```lua import("core.ui.textdialog") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建文本对话框 local dialog = textdialog:new("text", rect{10, 5, 60, 20}, "文本对话框") -- 设置文本内容 dialog:text():text_set("这是一个文本对话框示例。\n您可以显示多行文本内容。\n当文本内容超出显示区域时,可以启用滚动功能。") -- 启用滚动条 dialog:option_set("scrollable", true) -- 添加按钮 dialog:button_add("ok", "< 确定 >", function (v) self:quit() end) self:insert(dialog) self._dialog = dialog end function demo:on_resize() if self._dialog then self._dialog:bounds_set(rect{10, 5, self:width() - 20, self:height() - 10}) end application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/textedit.md --- # core.ui.textedit This module provides a text editor component for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.textedit")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `textedit` module extends `textarea` and provides a text editor with editable text. It supports multi-line editing, cursor display, and keyboard input. ## textedit:new * Create a new text editor instance #### Function Prototype ::: tip API ```lua textedit:new(name: , bounds: , text?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Text editor name string | | bounds | Required. Text editor bounds rectangle | | text | Optional. Initial text content | #### Return Value | Type | Description | |------|-------------| | textedit | Returns a text editor instance | #### Usage Create a text editor: ```lua import("core.ui.textedit") import("core.ui.rect") local textedit = textedit:new("editor", rect{10, 5, 50, 10}, "Initial text") ``` ## textedit:text\_set * Set text content #### Function Prototype ::: tip API ```lua textedit:text_set(text: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | text | Required. Text content to set | #### Return Value | Type | Description | |------|-------------| | textedit | Returns the text editor instance (for chaining) | #### Usage Set or update text content: ```lua textedit:text_set("New text content") ``` After setting text, it automatically scrolls to the bottom and displays the cursor. ## textedit:on\_event * Handle keyboard events #### Function Prototype ::: tip API ```lua textedit:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if the event was handled, otherwise returns `false` | #### Usage The text editor automatically handles the following keyboard events: * Regular characters: Insert characters * `Enter`: Insert newline (when multi-line mode is enabled) * `Backspace`: Delete character before cursor (supports UTF-8 characters) * `Ctrl+V`: Paste clipboard content * `Up/Down`: Scroll text Here is a complete text editor usage example: ```lua import("core.ui.textedit") import("core.ui.window") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create window local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "Text Editor Demo") -- Create text editor local editor = textedit:new("editor", rect{2, 2, 50, 15}, "Enter text here...") -- Set text attribute editor:textattr_set("white on blue") -- Add text editor to window panel local panel = win:panel() panel:insert(editor) -- Select text editor panel:select(editor) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/textedit.md --- # core.ui.textedit 此模块提供用于终端 UI 系统中的文本编辑器组件。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.textedit")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `textedit` 模块继承自 `textarea`,提供可编辑文本的文本编辑器。支持多行编辑、光标显示和键盘输入。 ## textedit:new * 创建新的文本编辑器实例 #### 函数原型 ::: tip API ```lua textedit:new(name: , bounds: , text?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。文本编辑器名称字符串 | | bounds | 必需。文本编辑器边界矩形 | | text | 可选。初始文本内容 | #### 返回值说明 | 类型 | 描述 | |------|------| | textedit | 返回文本编辑器实例 | #### 用法说明 创建文本编辑器: ```lua import("core.ui.textedit") import("core.ui.rect") local textedit = textedit:new("editor", rect{10, 5, 50, 10}, "初始文本") ``` ## textedit:text\_set * 设置文本内容 #### 函数原型 ::: tip API ```lua textedit:text_set(text: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | text | 必需。要设置的文本内容 | #### 返回值说明 | 类型 | 描述 | |------|------| | textedit | 返回文本编辑器实例(用于链式调用) | #### 用法说明 设置或更新文本内容: ```lua textedit:text_set("新文本内容") ``` 设置文本后会自动滚动到底部并显示光标。 ## textedit:on\_event * 处理键盘事件 #### 函数原型 ::: tip API ```lua textedit:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件已处理返回 `true`,否则返回 `false` | #### 用法说明 文本编辑器自动处理以下键盘事件: * 普通字符:输入字符 * `Enter`:插入换行符(当启用了多行模式时) * `Backspace`:删除光标前一个字符(支持 UTF-8 字符) * `Ctrl+V`:粘贴剪贴板内容 * `Up/Down`:滚动文本 以下是一个完整的文本编辑器使用示例: ```lua import("core.ui.textedit") import("core.ui.window") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "文本编辑器演示") -- 创建文本编辑器 local editor = textedit:new("editor", rect{2, 2, 50, 15}, "在这里输入文本...") -- 设置文本属性 editor:textattr_set("white on blue") -- 将文本编辑器添加到窗口面板 local panel = win:panel() panel:insert(editor) -- 选择文本编辑器 panel:select(editor) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/view.md --- # core.ui.view This module provides the base view component for the terminal UI system. ::: tip TIP To use this module, you need to import it first: `import("core.ui.view")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `view` module is the base class for all UI components. It extends `object` and provides core view functionality including drawing, event handling, state management, option management, and attribute management. ## view:new * Create a new view instance #### Function Prototype ::: tip API ```lua view:new(name: , bounds: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. View name string | | bounds | Required. View bounds rectangle | | ... | Variable arguments | #### Return Value | Type | Description | |------|-------------| | view | Returns a view instance | #### Usage Create a view instance: ```lua import("core.ui.view") import("core.ui.rect") local v = view:new("myview", rect{1, 1, 80, 25}) ``` ## view:name * Get the view name #### Function Prototype ::: tip API ```lua view:name() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the view name | #### Usage Get the view name: ```lua local name = v:name() print(name) -- Output: myview ``` ## view:bounds * Get the view bounds rectangle #### Function Prototype ::: tip API ```lua view:bounds() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | rect | Returns the view bounds rectangle | #### Usage Get the view bounds: ```lua local bounds = v:bounds() print(bounds:sx(), bounds:sy(), bounds:ex(), bounds:ey()) ``` ## view:bounds\_set * Set the view bounds rectangle #### Function Prototype ::: tip API ```lua view:bounds_set(bounds: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | bounds | Required. New bounds rectangle | #### Return Value No return value #### Usage Set the view bounds: ```lua v:bounds_set(rect{10, 5, 50, 20}) ``` ## view:width * Get the view width #### Function Prototype ::: tip API ```lua view:width() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns the view width | #### Usage Get the view width: ```lua local w = v:width() ``` ## view:height * Get the view height #### Function Prototype ::: tip API ```lua view:height() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | number | Returns the view height | #### Usage Get the view height: ```lua local h = v:height() ``` ## view:parent * Get the parent view #### Function Prototype ::: tip API ```lua view:parent() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | view | Returns the parent view instance or `nil` | #### Usage Get the parent view: ```lua local parent = v:parent() ``` ## view:application * Get the application instance #### Function Prototype ::: tip API ```lua view:application() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | application | Returns the application instance | #### Usage Get the application instance: ```lua local app = v:application() app:quit() ``` ## view:canvas * Get the view canvas #### Function Prototype ::: tip API ```lua view:canvas() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | canvas | Returns the canvas instance | #### Usage Get the canvas for drawing operations: ```lua local canvas = v:canvas() canvas:move(0, 0):putstr("Hello") ``` ## view:state * Get the view state #### Function Prototype ::: tip API ```lua view:state(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. State name | #### Return Value | Type | Description | |------|-------------| | any | Returns the state value | #### Usage Get the view state. Common states include: * `visible`: Whether visible * `cursor_visible`: Whether cursor is visible * `selected`: Whether selected * `focused`: Whether focused ```lua local is_visible = v:state("visible") ``` ## view:state\_set * Set the view state #### Function Prototype ::: tip API ```lua view:state_set(name: , enable: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. State name | | enable | Required. Whether to enable | #### Return Value | Type | Description | |------|-------------| | view | Returns the view instance (for chaining) | #### Usage Set the view state: ```lua v:state_set("visible", true) ``` ## view:option * Get the view option #### Function Prototype ::: tip API ```lua view:option(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Option name | #### Return Value | Type | Description | |------|-------------| | any | Returns the option value | #### Usage Get the view option. Common options include: * `selectable`: Whether selectable * `mouseable`: Whether mouse support ```lua local is_selectable = v:option("selectable") ``` ## view:option\_set * Set the view option #### Function Prototype ::: tip API ```lua view:option_set(name: , enable: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Option name | | enable | Required. Whether to enable | #### Return Value No return value #### Usage Set the view option: ```lua v:option_set("selectable", true) ``` ## view:attr * Get the view attribute #### Function Prototype ::: tip API ```lua view:attr(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Attribute name | #### Return Value | Type | Description | |------|-------------| | any | Returns the attribute value | #### Usage Get the view attribute: ```lua local bg = v:attr("background") ``` ## view:attr\_set * Set the view attribute #### Function Prototype ::: tip API ```lua view:attr_set(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Attribute name | | value | Required. Attribute value | #### Return Value | Type | Description | |------|-------------| | view | Returns the view instance (for chaining) | #### Usage Set the view attribute: ```lua v:attr_set("background", "blue") ``` ## view:background * Get the background color #### Function Prototype ::: tip API ```lua view:background() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | string | Returns the background color name | #### Usage Get the background color: ```lua local bg = v:background() ``` ## view:background\_set * Set the background color #### Function Prototype ::: tip API ```lua view:background_set(color: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | color | Required. Color name | #### Return Value | Type | Description | |------|-------------| | view | Returns the view instance (for chaining) | #### Usage Set the background color: ```lua v:background_set("blue") ``` ## view:action\_set * Set the action handler #### Function Prototype ::: tip API ```lua view:action_set(name: , on_action: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Action name | | on\_action | Required. Action handler function | #### Return Value | Type | Description | |------|-------------| | view | Returns the view instance (for chaining) | #### Usage Set the action handler: ```lua import("core.ui.action") v:action_set(action.ac_on_clicked, function (view) print("View clicked") end) ``` ## view:action\_on * Trigger an action #### Function Prototype ::: tip API ```lua view:action_on(name: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Action name | | ... | Variable arguments, passed to action handler | #### Return Value | Type | Description | |------|-------------| | any | Returns the action handler's return value | #### Usage Trigger an action: ```lua import("core.ui.action") v:action_on(action.ac_on_clicked) ``` ## view:invalidate * Invalidate the view (needs redraw) #### Function Prototype ::: tip API ```lua view:invalidate(bounds?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | bounds | Optional. Whether resize is needed | #### Return Value No return value #### Usage Invalidate the view to trigger redraw: ```lua v:invalidate() -- Mark as needing redraw v:invalidate(true) -- Mark as needing resize ``` ## view:show * Show or hide the view #### Function Prototype ::: tip API ```lua view:show(visible: , opt?:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | visible | Required. Whether to show | | opt | Optional. Option table, supports: `{focused = true}` | #### Return Value No return value #### Usage Show or hide the view: ```lua v:show(true) -- Show v:show(false) -- Hide v:show(true, {focused = true}) -- Show and focus ``` ## view:cursor\_move * Move cursor position #### Function Prototype ::: tip API ```lua view:cursor_move(x: , y: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | x | Required. Cursor X coordinate | | y | Required. Cursor Y coordinate | #### Return Value | Type | Description | |------|-------------| | view | Returns the view instance (for chaining) | #### Usage Move cursor position: ```lua v:cursor_move(10, 5) ``` ## view:cursor\_show * Show or hide cursor #### Function Prototype ::: tip API ```lua view:cursor_show(visible: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | visible | Required. Whether to show cursor | #### Return Value | Type | Description | |------|-------------| | view | Returns the view instance (for chaining) | #### Usage Show or hide cursor: ```lua v:cursor_show(true) -- Show cursor v:cursor_show(false) -- Hide cursor ``` ## view:on\_draw * Draw the view #### Function Prototype ::: tip API ```lua view:on_draw(transparent: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | transparent | Optional. Whether to draw transparently | #### Return Value No return value #### Usage This method is automatically called when drawing the view. Override in subclasses to implement custom drawing: ```lua function my_view:on_draw(transparent) -- Call parent method to draw background view.on_draw(self, transparent) -- Custom drawing content local canvas = self:canvas() canvas:move(0, 0):putstr("My View") end ``` ## view:on\_event * Handle events #### Function Prototype ::: tip API ```lua view:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if event was handled, otherwise returns `false` | #### Usage This method is automatically called when handling events. Override in subclasses to implement custom event handling: ```lua function my_view:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Enter" then -- Handle Enter key return true end return view.on_event(self, e) end ``` Here is a complete custom view example: ```lua import("core.ui.view") import("core.ui.window") import("core.ui.rect") import("core.ui.application") import("core.ui.event") import("core.ui.action") -- Define custom view local myview = myview or view() function myview:init(name, bounds) view.init(self, name, bounds) self:background_set("cyan") self:option_set("selectable", true) end function myview:on_draw(transparent) view.on_draw(self, transparent) local canvas = self:canvas() local textattr = curses.calc_attr({"yellow", "bold"}) canvas:attr(textattr):move(0, 0):putstr("Custom View") end function myview:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Enter" then print("Custom view activated") return true end return view.on_event(self, e) end -- Use custom view local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- Create window local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "Custom View Demo") -- Create custom view local custom = myview:new("custom", rect{10, 5, 40, 10}) -- Add view to window panel local panel = win:panel() panel:insert(custom) panel:select(custom) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /zh/api/scripts/extension-modules/core/ui/view.md --- # core.ui.view 此模块为终端 UI 系统提供基础视图组件。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.view")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `view` 模块是所有 UI 组件的基础类。它继承自 `object`,提供视图的基础功能,包括绘制、事件处理、状态管理、选项管理、属性管理等。 ## view:new * 创建新的视图实例 #### 函数原型 ::: tip API ```lua view:new(name: , bounds: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。视图名称字符串 | | bounds | 必需。视图边界矩形 | | ... | 可变参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回视图实例 | #### 用法说明 创建视图实例: ```lua import("core.ui.view") import("core.ui.rect") local v = view:new("myview", rect{1, 1, 80, 25}) ``` ## view:name * 获取视图名称 #### 函数原型 ::: tip API ```lua view:name() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回视图名称 | #### 用法说明 获取视图名称: ```lua local name = v:name() print(name) -- 输出: myview ``` ## view:bounds * 获取视图边界矩形 #### 函数原型 ::: tip API ```lua view:bounds() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | rect | 返回视图边界矩形 | #### 用法说明 获取视图边界: ```lua local bounds = v:bounds() print(bounds:sx(), bounds:sy(), bounds:ex(), bounds:ey()) ``` ## view:bounds\_set * 设置视图边界矩形 #### 函数原型 ::: tip API ```lua view:bounds_set(bounds: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | bounds | 必需。新的边界矩形 | #### 返回值说明 无返回值 #### 用法说明 设置视图边界: ```lua v:bounds_set(rect{10, 5, 50, 20}) ``` ## view:width * 获取视图宽度 #### 函数原型 ::: tip API ```lua view:width() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回视图宽度 | #### 用法说明 获取视图宽度: ```lua local w = v:width() ``` ## view:height * 获取视图高度 #### 函数原型 ::: tip API ```lua view:height() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回视图高度 | #### 用法说明 获取视图高度: ```lua local h = v:height() ``` ## view:parent * 获取父视图 #### 函数原型 ::: tip API ```lua view:parent() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回父视图实例或 `nil` | #### 用法说明 获取父视图: ```lua local parent = v:parent() ``` ## view:application * 获取应用程序实例 #### 函数原型 ::: tip API ```lua view:application() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | application | 返回应用程序实例 | #### 用法说明 获取应用程序实例: ```lua local app = v:application() app:quit() ``` ## view:canvas * 获取视图画布 #### 函数原型 ::: tip API ```lua view:canvas() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | canvas | 返回画布实例 | #### 用法说明 获取画布进行绘制操作: ```lua local canvas = v:canvas() canvas:move(0, 0):putstr("Hello") ``` ## view:state * 获取视图状态 #### 函数原型 ::: tip API ```lua view:state(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。状态名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 返回状态值 | #### 用法说明 获取视图状态,常见状态包括: * `visible`:是否可见 * `cursor_visible`:光标是否可见 * `selected`:是否被选中 * `focused`:是否获得焦点 ```lua local is_visible = v:state("visible") ``` ## view:state\_set * 设置视图状态 #### 函数原型 ::: tip API ```lua view:state_set(name: , enable: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。状态名称 | | enable | 必需。是否启用 | #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回视图实例(用于链式调用) | #### 用法说明 设置视图状态: ```lua v:state_set("visible", true) ``` ## view:option * 获取视图选项 #### 函数原型 ::: tip API ```lua view:option(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。选项名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 返回选项值 | #### 用法说明 获取视图选项,常见选项包括: * `selectable`:是否可选择 * `mouseable`:是否支持鼠标 ```lua local is_selectable = v:option("selectable") ``` ## view:option\_set * 设置视图选项 #### 函数原型 ::: tip API ```lua view:option_set(name: , enable: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。选项名称 | | enable | 必需。是否启用 | #### 返回值说明 无返回值 #### 用法说明 设置视图选项: ```lua v:option_set("selectable", true) ``` ## view:attr * 获取视图属性 #### 函数原型 ::: tip API ```lua view:attr(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。属性名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 返回属性值 | #### 用法说明 获取视图属性: ```lua local bg = v:attr("background") ``` ## view:attr\_set * 设置视图属性 #### 函数原型 ::: tip API ```lua view:attr_set(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。属性名称 | | value | 必需。属性值 | #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回视图实例(用于链式调用) | #### 用法说明 设置视图属性: ```lua v:attr_set("background", "blue") ``` ## view:background * 获取背景颜色 #### 函数原型 ::: tip API ```lua view:background() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回背景颜色名称 | #### 用法说明 获取背景颜色: ```lua local bg = v:background() ``` ## view:background\_set * 设置背景颜色 #### 函数原型 ::: tip API ```lua view:background_set(color: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | color | 必需。颜色名称 | #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回视图实例(用于链式调用) | #### 用法说明 设置背景颜色: ```lua v:background_set("blue") ``` ## view:action\_set * 设置动作处理器 #### 函数原型 ::: tip API ```lua view:action_set(name: , on_action: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。动作名称 | | on\_action | 必需。动作处理函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回视图实例(用于链式调用) | #### 用法说明 设置动作处理器: ```lua import("core.ui.action") v:action_set(action.ac_on_clicked, function (view) print("视图被点击") end) ``` ## view:action\_on * 触发动作 #### 函数原型 ::: tip API ```lua view:action_on(name: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。动作名称 | | ... | 可变参数,传递给动作处理器 | #### 返回值说明 | 类型 | 描述 | |------|------| | any | 返回动作处理器的返回值 | #### 用法说明 触发动作: ```lua import("core.ui.action") v:action_on(action.ac_on_clicked) ``` ## view:invalidate * 使视图无效化(需要重绘) #### 函数原型 ::: tip API ```lua view:invalidate(bounds?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | bounds | 可选。是否需要调整大小 | #### 返回值说明 无返回值 #### 用法说明 使视图无效化以便重绘: ```lua v:invalidate() -- 标记需要重绘 v:invalidate(true) -- 标记需要调整大小 ``` ## view:show * 显示或隐藏视图 #### 函数原型 ::: tip API ```lua view:show(visible: , opt?:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | visible | 必需。是否显示 | | opt | 可选。选项表格,支持:`{focused = true}` | #### 返回值说明 无返回值 #### 用法说明 显示或隐藏视图: ```lua v:show(true) -- 显示 v:show(false) -- 隐藏 v:show(true, {focused = true}) -- 显示并获得焦点 ``` ## view:cursor\_move * 移动光标位置 #### 函数原型 ::: tip API ```lua view:cursor_move(x: , y: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | x | 必需。光标 X 坐标 | | y | 必需。光标 Y 坐标 | #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回视图实例(用于链式调用) | #### 用法说明 移动光标位置: ```lua v:cursor_move(10, 5) ``` ## view:cursor\_show * 显示或隐藏光标 #### 函数原型 ::: tip API ```lua view:cursor_show(visible: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | visible | 必需。是否显示光标 | #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回视图实例(用于链式调用) | #### 用法说明 显示或隐藏光标: ```lua v:cursor_show(true) -- 显示光标 v:cursor_show(false) -- 隐藏光标 ``` ## view:on\_draw * 绘制视图 #### 函数原型 ::: tip API ```lua view:on_draw(transparent: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | transparent | 可选。是否透明绘制 | #### 返回值说明 无返回值 #### 用法说明 绘制视图时自动调用此方法。可以在子类中重写以实现自定义绘制: ```lua function my_view:on_draw(transparent) -- 调用父类方法绘制背景 view.on_draw(self, transparent) -- 自定义绘制内容 local canvas = self:canvas() canvas:move(0, 0):putstr("My View") end ``` ## view:on\_event * 处理事件 #### 函数原型 ::: tip API ```lua view:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果事件已处理返回 `true`,否则返回 `false` | #### 用法说明 处理事件时自动调用此方法。可以在子类中重写以实现自定义事件处理: ```lua function my_view:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Enter" then -- 处理 Enter 键 return true end return view.on_event(self, e) end ``` 以下是一个完整的自定义视图示例: ```lua import("core.ui.view") import("core.ui.window") import("core.ui.rect") import("core.ui.application") import("core.ui.event") import("core.ui.action") -- 定义自定义视图 local myview = myview or view() function myview:init(name, bounds) view.init(self, name, bounds) self:background_set("cyan") self:option_set("selectable", true) end function myview:on_draw(transparent) view.on_draw(self, transparent) local canvas = self:canvas() local textattr = curses.calc_attr({"yellow", "bold"}) canvas:attr(textattr):move(0, 0):putstr("Custom View") end function myview:on_event(e) if e.type == event.ev_keyboard and e.key_name == "Enter" then print("自定义视图被激活") return true end return view.on_event(self, e) end -- 使用自定义视图 local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "自定义视图演示") -- 创建自定义视图 local custom = myview:new("custom", rect{10, 5, 40, 10}) -- 将视图添加到窗口面板 local panel = win:panel() panel:insert(custom) panel:select(custom) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/extension-modules/core/ui/window.md --- # core.ui.window This module provides a window with border, title, and panel support. ::: tip TIP To use this module, you need to import it first: `import("core.ui.window")` ::: ::: tip NOTE The UI module is primarily used for xmake's internal `xmake f --menu` menu-based visual configuration. It provides basic UI components that can also be used by users to implement their own terminal UIs. ::: The `window` module extends `panel` and provides a complete window container with: * Border and shadow support * Title bar (centered) * Content panel for child views * Tab key navigation between focusable views ## window:new * Create a new window instance #### Function Prototype ::: tip API ```lua window:new(name: , bounds: , title?: , shadow?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Window name string | | bounds | Required. Window bounds rectangle | | title | Optional. Window title string (centered at top) | | shadow | Optional. Show shadow effect (`true` or `false`, default `false`) | #### Return Value | Type | Description | |------|-------------| | window | Returns a window instance | #### Usage Create a window with title and shadow: ```lua import("core.ui.window") import("core.ui.rect") local win = window:new("main", rect{10, 5, 60, 20}, "Main Window", true) ``` ## window:frame * Get the frame panel #### Function Prototype ::: tip API ```lua window:frame() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | panel | Returns the frame panel instance | #### Usage Access the frame panel to customize its appearance: ```lua local frame = win:frame() frame:background_set("cyan") ``` ## window:panel * Get the content panel #### Function Prototype ::: tip API ```lua window:panel() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | panel | Returns the content panel instance | #### Usage Access the content panel to add child views: ```lua local panel = win:panel() panel:insert(label:new("label1", rect{1, 1, 20, 1}, "Hello")) ``` ## window:title * Get the title label #### Function Prototype ::: tip API ```lua window:title() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | label | Returns the title label instance or `nil` if no title was set | #### Usage Access and customize the title label: ```lua local title = win:title() if title then title:text_set("New Title") title:textattr_set("red bold") end ``` ## window:shadow * Get the shadow view #### Function Prototype ::: tip API ```lua window:shadow() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | view | Returns the shadow view instance or `nil` if shadow was not enabled | #### Usage Access the shadow view: ```lua local shadow = win:shadow() if shadow then shadow:background_set("gray") end ``` ## window:border * Get the border component #### Function Prototype ::: tip API ```lua window:border() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | border | Returns the border instance | #### Usage Access the border component: ```lua local border = win:border() ``` ## window:on\_event * Handle keyboard events for tab navigation #### Function Prototype ::: tip API ```lua window:on_event(e: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | e | Required. Event object | #### Return Value | Type | Description | |------|-------------| | boolean | Returns `true` if Tab key was handled, `nil` otherwise | #### Usage The window automatically handles Tab key navigation between focusable views in the panel: ```lua -- Tab key is automatically handled to navigate focus ``` ## window:on\_resize * Handle window resize #### Function Prototype ::: tip API ```lua window:on_resize() ``` ::: #### Parameter Description No parameters #### Return Value No return value #### Usage This is automatically called when the window is resized. It adjusts: * Frame bounds * Shadow bounds (if enabled) * Border bounds * Title position (centered) * Panel bounds (adjusted with 1 unit inset) --- --- url: /zh/api/scripts/extension-modules/core/ui/window.md --- # core.ui.window 此模块提供带有边框、标题和面板支持的窗口。 ::: tip 提示 使用此模块需要先导入:`import("core.ui.window")` ::: ::: tip 注意 UI 模块主要用于 xmake 内部的 `xmake f --menu` 菜单可视化配置。它提供基础的 UI 组件,当然,用户也可以用来实现一些自己的终端 UI。 ::: `window` 模块继承自 `panel`,提供完整的窗口容器,包括: * 边框和阴影支持 * 标题栏(居中) * 用于子视图的内容面板 * Tab 键在可聚焦视图间导航 ## window:new * 创建新的窗口实例 #### 函数原型 ::: tip API ```lua window:new(name: , bounds: , title?: , shadow?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。窗口名称字符串 | | bounds | 必需。窗口边界矩形 | | title | 可选。窗口标题字符串(顶部居中显示) | | shadow | 可选。显示阴影效果(`true` 或 `false`,默认 `false`) | #### 返回值说明 | 类型 | 描述 | |------|------| | window | 返回窗口实例 | #### 用法说明 创建带标题和阴影的窗口: ```lua import("core.ui.window") import("core.ui.rect") local win = window:new("main", rect{10, 5, 60, 20}, "主窗口", true) ``` ## window:frame * 获取框架面板 #### 函数原型 ::: tip API ```lua window:frame() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | panel | 返回框架面板实例 | #### 用法说明 访问框架面板以自定义其外观: ```lua local frame = win:frame() frame:background_set("cyan") ``` ## window:panel * 获取内容面板 #### 函数原型 ::: tip API ```lua window:panel() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | panel | 返回内容面板实例 | #### 用法说明 访问内容面板以添加子视图: ```lua local panel = win:panel() panel:insert(label:new("label1", rect{1, 1, 20, 1}, "你好")) ``` ## window:title * 获取标题标签 #### 函数原型 ::: tip API ```lua window:title() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | label | 返回标题标签实例,如果未设置标题则为 `nil` | #### 用法说明 访问并自定义标题标签: ```lua local title = win:title() if title then title:text_set("新标题") title:textattr_set("red bold") end ``` ## window:shadow * 获取阴影视图 #### 函数原型 ::: tip API ```lua window:shadow() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | view | 返回阴影视图实例,如果未启用阴影则为 `nil` | #### 用法说明 访问阴影视图: ```lua local shadow = win:shadow() if shadow then shadow:background_set("gray") end ``` ## window:border * 获取边框组件 #### 函数原型 ::: tip API ```lua window:border() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | border | 返回边框实例 | #### 用法说明 访问边框组件: ```lua local border = win:border() ``` ## window:on\_event * 处理 Tab 键导航的键盘事件 #### 函数原型 ::: tip API ```lua window:on_event(e: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | e | 必需。事件对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 如果 Tab 键被处理返回 `true`,否则返回 `nil` | #### 用法说明 窗口自动处理面板中可聚焦视图之间的 Tab 键导航: ```lua -- Tab 键自动处理以导航焦点 ``` ## window:on\_resize * 处理窗口调整大小 #### 函数原型 ::: tip API ```lua window:on_resize() ``` ::: #### 参数说明 无参数 #### 返回值说明 无返回值 #### 用法说明 窗口调整大小时自动调用此方法。它会调整: * 框架边界 * 阴影边界(如果启用) * 边框边界 * 标题位置(居中) * 面板边界(向内缩进 1 个单位) 以下是一个完整示例: ```lua import("core.ui.window") import("core.ui.label") import("core.ui.rect") import("core.ui.application") local demo = application() function demo:init() application.init(self, "demo") self:background_set("blue") -- 创建带标题和阴影的主窗口 local win = window:new("main", rect{1, 1, self:width() - 1, self:height() - 1}, "主窗口", true) -- 向面板添加内容 local panel = win:panel() panel:insert(label:new("label1", rect{2, 2, 30, 1}, "欢迎使用终端 UI!")) panel:insert(label:new("label2", rect{2, 4, 30, 1}, "这是一个窗口示例。")) panel:insert(label:new("label3", rect{2, 6, 30, 1}, "Tab 键在视图之间导航。")) self:insert(win) self._win = win end function demo:on_resize() self._win:bounds_set(rect{1, 1, self:width() - 1, self:height() - 1}) application.on_resize(self) end function main(...) demo:run(...) end ``` --- --- url: /api/scripts/builtin-modules/coroutine.md --- # coroutine The coroutine module is a native module of lua. For use, see: [lua official manual](https://www.lua.org/manual/5.1/manual.html#5.2) ## coroutine.create * Create a new coroutine #### Function Prototype ::: tip API ```lua coroutine.create(f: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | f | Function to create coroutine | #### Usage Creates a new coroutine with function `f`. Returns a thread object that represents the coroutine. ```lua local co = coroutine.create(function() print("Hello from coroutine") end) ``` ## coroutine.resume * Resume execution of a coroutine #### Function Prototype ::: tip API ```lua coroutine.resume(co: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | co | Coroutine thread | | ... | Arguments to pass to the coroutine | #### Usage Starts or continues the execution of coroutine `co`. Returns true if the coroutine execution is successful, false otherwise. ```lua local success, result = coroutine.resume(co, "arg1", "arg2") ``` ## coroutine.yield * Yield execution to the caller #### Function Prototype ::: tip API ```lua coroutine.yield(...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ... | Values to return to the caller | #### Usage Suspends the execution of the calling coroutine and returns values to the caller. ```lua coroutine.yield("value1", "value2") ``` ## coroutine.status * Get the status of a coroutine #### Function Prototype ::: tip API ```lua coroutine.status(co: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | co | Coroutine thread | #### Usage Returns the status of coroutine `co`: "suspended", "running", "normal", or "dead". ```lua local status = coroutine.status(co) ``` ## coroutine.wrap * Create a coroutine wrapper function #### Function Prototype ::: tip API ```lua coroutine.wrap(f: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | f | Function to create coroutine | #### Usage Creates a new coroutine with function `f` and returns a function that resumes the coroutine each time it is called. ```lua local func = coroutine.wrap(function() return "Hello" end) local result = func() ``` ## coroutine.running * Get the running coroutine #### Function Prototype ::: tip API ```lua coroutine.running() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the running coroutine, or nil when called by the main thread. ```lua local co = coroutine.running() ``` --- --- url: /zh/api/scripts/builtin-modules/coroutine.md --- # coroutine 协程模块是lua原生自带的模块,具使用见:[lua官方手册](https://www.lua.org/manual/5.1/manual.html#5.2) ## coroutine.create * 创建一个新的协程 #### 函数原型 ::: tip API ```lua coroutine.create(f: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | f | 创建协程的函数 | #### 用法说明 创建一个带有函数 `f` 的新协程。返回一个代表协程的线程对象。 ```lua local co = coroutine.create(function() print("Hello from coroutine") end) ``` ## coroutine.resume * 恢复协程的执行 #### 函数原型 ::: tip API ```lua coroutine.resume(co: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | co | 协程线程 | | ... | 传递给协程的参数 | #### 用法说明 开始或继续执行协程 `co`。如果协程执行成功则返回 true,否则返回 false。 ```lua local success, result = coroutine.resume(co, "arg1", "arg2") ``` ## coroutine.yield * 将执行权让给调用者 #### 函数原型 ::: tip API ```lua coroutine.yield(...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ... | 返回给调用者的值 | #### 用法说明 暂停调用协程的执行并将值返回给调用者。 ```lua coroutine.yield("value1", "value2") ``` ## coroutine.status * 获取协程的状态 #### 函数原型 ::: tip API ```lua coroutine.status(co: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | co | 协程线程 | #### 用法说明 返回协程 `co` 的状态:"suspended"(挂起)、"running"(运行中)、"normal"(正常)或 "dead"(死亡)。 ```lua local status = coroutine.status(co) ``` ## coroutine.wrap * 创建协程包装函数 #### 函数原型 ::: tip API ```lua coroutine.wrap(f: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | f | 创建协程的函数 | #### 用法说明 创建一个带有函数 `f` 的新协程,并返回一个函数,每次调用该函数时都会恢复协程。 ```lua local func = coroutine.wrap(function() return "Hello" end) local result = func() ``` ## coroutine.running * 获取正在运行的协程 #### 函数原型 ::: tip API ```lua coroutine.running() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回正在运行的协程,如果在主线程中调用则返回 nil。 ```lua local co = coroutine.running() ``` --- --- url: /api/scripts/builtin-modules/cprint.md --- # cprint * Wrap color print terminal log #### Function Prototype ::: tip API ```lua cprint(...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ... | Variable arguments, can pass multiple values | #### Usage The behavior is similar to [print](/api/scripts/builtin-modules/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: ![cprint\_colors](/assets/img/cprint_colors.png) 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. . ![cprint\_emoji](/assets/img/cprint_emoji.png) 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: ```sh $ export XMAKE_COLORTERM=truecolor $ xmake --version ``` We can disable color output with `XMAKE_COLORTERM=nocolor`, or switch to plain theme to disable. `xmake g --theme=plain` --- --- url: /zh/api/scripts/builtin-modules/cprint.md --- # cprint * 换行彩色打印终端日志 #### 函数原型 ::: tip API ```lua cprint(...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ... | 可变参数,可传递多个值 | #### 用法说明 行为类似[print](/zh/api/scripts/builtin-modules/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') ``` 显示结果如下: ![cprint\_colors](/assets/img/cprint_colors.png) 跟颜色相关的描述,都放置在 `${ }` 里面,可以同时设置多个不同的属性,例如: ``` ${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的手势符号,是不是很炫哈。。 ![cprint\_emoji](/assets/img/cprint_emoji.png) 所有的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支持。 可以通过下面的命令来启用和测试: ```sh $ export XMAKE_COLORTERM=truecolor $ xmake --version ``` 我们也可以通过`XMAKE_COLORTERM=nocolor`来禁用色彩输出。 或者切换到 plain 主题来禁用它,`xmake g --theme=plain`。 --- --- url: /api/scripts/builtin-modules/cprintf.md --- # cprintf * No line feed color print terminal log #### Function Prototype ::: tip API ```lua cprintf(...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ... | Variable arguments, can pass multiple values | #### Usage This interface is similar to [cprint](/api/scripts/builtin-modules/cprint), the difference is that it does not wrap the output. --- --- url: /zh/api/scripts/builtin-modules/cprintf.md --- # cprintf * 无换行彩色打印终端日志 #### 函数原型 ::: tip API ```lua cprintf(...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ... | 可变参数,可传递多个值 | #### 用法说明 此接口类似[cprint](/zh/api/scripts/builtin-modules/cprint),区别就是不换行输出。 --- --- url: /guide/basic-commands/create-project.md --- # Create a project {#create-project} ## Introduction Xmake provides some common commands to allow users to easily and quickly create projects, as well as configure, compile, and run them. All commands can be viewed through `xmake -h`. The command format is as follows: ```sh xmake [action] [arguments] ... ``` Here, action is the subcommand provided by the xmake CLI, and for creating a project, it is `xmake create`. ## Create a C++ empty project First, we can try to create a `c++` console empty project named `hello`. ```sh $ xmake create hello create hello ... [+]: xmake.lua [+]: src/main.cpp [+]: .gitignore create ok! ``` After execution, a simple project structure will be generated: ``` hello ├── src │   └─main.cpp └── xmake.lua ``` This is the simplest project. It will generate a basic xmake.lua project configuration file, which is usually located in the project root directory and Xmake will automatically load it. ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("hello") set_kind("binary") add_files("src/*.cpp") -- FAQ -- ... ``` In addition, some comments are provided at the end of the file, which offer common configuration examples for quick reference. Then, we only need to enter the root directory of the hello project just created and execute the xmake command to complete the compilation. ```sh $ xmake [ 23%]: cache compiling.release src/main.cpp [ 47%]: linking.release hello [100%]: build ok, spent 2.696s ``` ## Specify language We can use the `-l [language]` parameter to specify the creation of projects in other languages, such as creating a C language project. ```sh $ xmake create -l c hello create hello ... [+]: xmake.lua [+]: src/main.c [+]: .gitignore create ok! ``` Or create an empty Rust project. ```sh $ xmake create -l rust hello create hello ... [+]: xmake.lua [+]: src/main.rs [+]: .gitignore create ok! ``` The complete list of supported languages can be viewed through `xmake create -h`. ```sh -l LANGUAGE, --language=LANGUAGE The project language (default: c++) - pascal - c++ - zig - go - nim - dlang - cuda - rust - kotlin - vala - swift - fortran - objc - c - objc++ ``` ## Specify the project template In addition, we can also use the `-t [template]` parameter to specify the type of project template to be created. For example, to create a static library project: ```sh $ xmake create -t ​​static test create test ... [+]: xmake.lua [+]: src/foo.cpp [+]: src/foo.h [+]: src/main.cpp [+]: .gitignore create ok! ``` ```sh $ xmake [ 23%]: cache compiling.release src/main.cpp [ 23%]: cache compiling.release src/foo.cpp [ 35%]: linking.release libfoo.a [ 71%]: linking.release test [100%]: build ok, spent 1.795s ``` The complete template list can also be viewed through `xmake create -h`. ```sh -t TEMPLATE, --template=TEMPLATE Select the project template id or name of the given language. (default: console) - console: pascal, c++, zig, go, nim, dlang, cuda, rust, kotlin, vala, swift, fortran, objc, c, objc++ - module.binary: c++, c - module.shared: c++, c - qt.console: c++ - qt.quickapp: c++ - qt.quickapp_static: c++ - qt.shared: c++ - qt.static: c++ - qt.widgetapp: c++ - qt.widgetapp_static: c++ - shared: pascal, c++, zig, nim, dlang, cuda, kotlin, vala, fortran, c - static: c++, zig, go, nim, dlang, cuda, rust, kotlin, vala, fortran, c - tbox.console: c++, c - tbox.shared: c++, c - tbox.static: c++, c - wxwidgets: c++ - xcode.bundle: objc, objc++ - xcode.framework: objc, objc++ - xcode.iosapp: objc - xcode.iosapp_with_framework: objc - xcode.macapp: objc - xcode.macapp_with_framework: objc - xmake.cli: c++, c ``` Among them, the most commonly used are templates for creating console programs (console), static libraries (static), and dynamic libraries (shared). --- --- url: /guide/basic-commands/cross-compilation.md --- # Cross Compilation Generally, if we need to compile and generate object files that can be run on other devices in the current PC environment, we need to compile and generate them through the corresponding cross-compilation toolchain, such as compiling Linux programs on Windows/macOS, or compiling object files for other embedded devices, etc. The usual cross-compilation toolchain is based on GCC/Clang, and most of them have a structure similar to the following: ``` /home/toolchains_sdkdir - bin - arm-linux-armeabi-gcc - arm-linux-armeabi-ld - ... - lib - libxxx.a - include - xxx.h ``` Each toolchain has a corresponding include/lib directory, which is used to place some system libraries and header files, such as libc, stdc++, etc., and a series of tools for compiling are placed under the bin directory. For example: ``` arm-linux-armeabi-ar arm-linux-armeabi-as arm-linux-armeabi-c++ arm-linux-armeabi-cpp arm-linux-armeabi-g++ arm-linux-armeabi-gcc arm-linux-armeabi-ld arm-linux-armeabi-nm arm-linux-armeabi-strip ``` The `arm-linux-armeabi-` prefix is the cross prefix, which is used to mark the target platform and architecture, and is mainly used to distinguish it from the host's own GCC/Clang. The gcc/g++ inside is the C/C++ compiler, which can also be used as a linker. When linking, it will internally call ld to link, and automatically add some C++ libraries. cpp is a preprocessor, as is is an assembler, ar is used to generate a static library, and strip is used to remove some symbol information, making the target program smaller. nm is used to view the list of exported symbols. ## Automatic detection and compilation If our cross-compilation toolchain has the above structure, Xmake will automatically detect and identify the structure of the SDK, extract the cross prefix and include/lib path location. Users usually do not need to do additional parameter settings, just configure the SDK root directory to compile, for example: ```sh $ xmake f -p cross --sdk=/home/toolchains_sdkdir $ xmake ``` Here, `-p cross` is used to specify that the current platform is a cross-compilation platform, and `--sdk=` is used to specify the root directory of the cross toolchain. Note: We can also specify the `-p linux` platform to configure cross compilation. The effect is the same, the only difference is that the name of the linux platform is additionally identified, which is convenient for `xmake.lua` to determine the platform by `is_plat("linux")`. At this time, Xmake will automatically detect the cross prefix of gcc and other compilers: `arm-linux-armeabi-`, and when compiling, it will also automatically add search options for `link library` and `header files`: ``` -I/home/toolchains_sdkdir/include -L/home/toolchains_sdkdir/lib ``` These are handled automatically by Xmake; there is no need to configure them manually. ## Manually configure and compile If the above automatic detection fails for some toolchains, you need to manually set some configuration parameters related to cross compilation to adapt to these special toolchains. I will explain how to configure them one by one. ## Set toolchain bin directory For irregular toolchain directory structures, simply setting the [--sdk](#sdk) option may not be enough. You can continue to set the location of the bin directory of the toolchain through this option. For example, for some special cross toolchains, the compiler bin directory is not in the `/home/toolchains_sdkdir/bin` position, but is instead in `/usr/opt/bin` At this time, we can add the parameter setting of the bin directory on the basis of setting the sdk parameter to adjust the bin directory of the toolchain. ```sh $ xmake f -p cross --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin $ xmake ``` ## Set tool prefix for cross toolchain Like aarch64-linux-android-, usually if you configure --sdk or --bin, Xmake will automatically detect it, and you don't need to set it manually. But for some very special toolchains, if there are multiple cross prefix tool bins in a directory at the same time, you need to manually set this configuration to distinguish which bin you need to choose. For example, there are two different compilers in the bin directory of toolchains: ``` /opt/bin - armv7-linux-gcc - aarch64-linux-gcc ``` If we now want to choose the armv7 version, then we can append `--cross=` to configure the compiler tool prefix name, for example: ```sh $ xmake f -p cross --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- ``` ## Set the c/c++ compiler If you want to further specify compilers, continue to add relevant compiler options, for example: ```sh $ xmake f -p cross --sdk=/user/toolsdk --cc=armv7-linux-clang --cxx=armv7-linux-clang++ ``` Of course, we can also specify the full path of the compiler. Note: If the CC/CXX environment variable exists, the value specified in the current environment variable will be used first. If the specified compiler name is not a name recognized by Xmake (such as gcc, clang, etc.), then the compiler tool detection will fail. At this time we can use: ```sh xmake f --cxx=clang++@/home/xxx/c++mips.exe ``` Set the c++ mips.exe compiler as the clang++ in usage and parameter options. ## Set the c/c++ linker If you want to further specify the linker, continue to add related linker options, for example: ```sh $ xmake f -p cross --sdk=/user/toolsdk --ld=armv7-linux-clang++ --sh=armv7-linux-clang++ --ar=armv7-linux-ar ``` ld specifies the executable program linker, sh specifies the shared library program linker, and ar specifies the archiver that generates the static library. Note: If there are LD/SH/AR environment variables, the value specified in the current environment variable will be used first. ## Set header file and library search directory If there are additional include/lib directories in the SDK that are not in the standard structure, resulting in cross compilation not finding the library and header files, then we can append the search path through `--includedirs` and `--linkdirs`, and then add additional link libraries via `--links`. ```sh $ xmake f -p cross --sdk=/usr/toolsdk --includedirs=/usr/toolsdk/xxx/include --linkdirs=/usr/toolsdk/xxx/lib --links=pthread ``` Note: If you want to specify multiple search directories, you can use `:` or `;` to separate them, which is the path separator of different host platforms. Use `:` under Linux/macOS, and `;` under Windows. ## Set compile and link options We can also configure some additional compilation and linking options through `--cflags`, `--cxxflags`, `--ldflags`, `--shflags` and `--arflags` according to the actual situation. * cflags: specify c compilation parameters * cxxflags: specify c++ compilation parameters * cxflags: specify c/c++ compilation parameters * asflags: specify assembler compilation parameters * ldflags: specify executable program link parameters * shflags: specify dynamic library program link parameters * arflags: specify the generation parameters of the static library e.g: ```sh $ xmake f -p cross --sdk=/usr/toolsdk --cflags="-DTEST -I/xxx/xxx" --ldflags="-lpthread" ``` ## Custom build platform If the target program has a corresponding platform to be specified after a cross toolchain is compiled, and it needs to be configured in `xmake.lua` according to different cross compilation platforms, and some additional compilation parameters need to be configured, then the `-p cross` setting above cannot meet the demand. In fact, the `-p/-plat=` parameter can also be set to other custom values. You only need to maintain the corresponding relationship with `is_plat`. All non-built-in platform names will default to cross-compilation mode, for example: ```sh $ xmake f -p myplat --sdk=/usr/local/arm-xxx-gcc/ $ xmake ``` We passed in the myplat custom platform name as the current cross-toolchain compilation platform, and then we set the corresponding settings for this platform in `xmake.lua`: ```lua if is_plat("myplat") then add_defines("TEST") end ``` In this way, Xmake can be easily extended to deal with various compilation platforms. Users can extend their own support for FreeBSD, NetBSD, SunOS and other cross-compiling platforms. Here is a cross-compilation configuration excerpted from porting libuv, for reference: ```lua -- for dragonfly/freebsd/netbsd/openbsd platform if is_plat("dragonfly", "freebsd", "netbsd", "openbsd") then add_files("src/unix/bsd-ifaddrs.c") add_files("src/unix/freebsd.c") add_files("src/unix/kqueue.c") add_files("src/unix/posix-hrtime.c") add_headerfiles("(include/uv-bsd.h)") end -- for sunos platform if is_plat("sunos") then add_files("src/unix/no-proctitle.c") add_files("src/unix/sunos.c") add_defines("__EXTENSIONS_", "_XOPEN_SOURCE=600") add_headerfiles("(include/uv-sunos.h)") end ``` Then, we can switch these platforms to compile: ```sh $ xmake f -p [dragonfly|freebsd|netbsd|openbsd|sunos] --sdk=/home/arm-xxx-gcc/ $ xmake ``` In addition, the built-in Linux platform also supports cross-compilation. If you do n't want to configure other platform names, you can cross-compile as the linux platform. ```sh $ xmake f -p linux --sdk=/usr/local/arm-xxx-gcc/ $ xmake ``` As long as the `--sdk=` and other parameters are set, the cross-compilation mode of the Linux platform will be enabled. ## Toolchain configuration For a complete list of tool chains, please execute the following command to view: ```sh $ xmake show -l toolchains ``` ::: tip NOTE This feature requires v2.3.4 or later to support ::: The above describes the general cross-compilation toolchain configuration. If some specific toolchains need to be imported into additional scenarios such as `--ldflags/--includedirs`, it is more cumbersome Therefore, xmake also has some common tool chains built-in, which can save the complicated configuration process of cross-compilation tool chain, and only need to execute: ```sh $ xmake f --toolchain=gnu-rm --sdk=/xxx/ $ xmake ``` You can quickly switch the designated cross-compilation tool chain. If this tool chain needs to add some specific flags settings, it will be automatically set up to simplify configuration. Among them, gnu-rm is the built-in GNU Arm Embedded Toolchain. For example, we can also quickly switch from the entire gcc tool chain to the clang or llvm tool chain, no longer need to make `xmake f --cc=clang --cxx=clang --ld=clang++` one by one. ```sh $ xmake f --toolchain=clang $ xmake ``` or ```sh $ xmake f --toolchain=llvm --sdk=/xxx/llvm $ xmake ``` The specific tool chains supported by Xmake can be viewed with the following command: ```sh $ xmake show -l toolchains xcode Xcode IDE vs VisualStudio IDE yasm The Yasm Modular Assembler clang A C language family frontend for LLVM go Go Programming Language Compiler dlang D Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit ndk Android NDK rust Rust Programming Language Compiler llvm A collection of modular and reusable compiler and toolchain technologies cross Common cross compilation toolchain nasm NASM Assembler gcc GNU Compiler Collection mingw Minimalist GNU for Windows gnu-rm GNU Arm Embedded Toolchain envs Environment variables toolchain fasm Flat Assembler ``` ### Custom toolchain In addition, we can also customize the toolchain in `xmake.lua`, and then specify the switch through `xmake f --toolchain=myclang`, for example: ```lua toolchain("myclang") set_kind("standalone") set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") - ... ``` For details about this piece, you can go to the [Custom Toolchain](/api/description/custom-toolchain). For more details, please see: [#780](https://github.com/xmake-io/xmake/issues/780) ### MingW Toolchain Compiling with the mingw toolchain is actually cross-compilation, but because this is more commonly used, Xmake specifically adds a mingw platform to quickly handle compilation using the mingw toolchain. Therefore, Xmake's toolchain detection for mingw will be more perfect. Under macos, basically even the sdk path does not need to be configured, and can be directly detected, only need to switch to the mingw platform to compile. ```sh $ xmake f -p mingw $ xmake -v configure { ld = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ ndk_stdcxx = true plat = mingw mingw = /usr/local/opt/mingw-w64 buildir = build arch = x86_64 xcode = /Applications/Xcode.app mode = release cxx = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc cross = x86_64-w64-mingw32- theme = default kind = static ccache = true host = macosx clean = true bin = /usr/local/opt/mingw-w64/bin } [ 0%]: cache compiling.release src/main.cpp /usr/local/bin/ccache /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc -c -fvisibility=hidden -O3 -m64 -o build/.objs/test/mingw/x86_64/release/src/main.cpp.obj src/main.cpp [100%]: linking.release test.exe /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ -o build/mingw/x86_64/release/test.exe build/.objs/test/mingw/x86_64/release/src/main.cpp.obj -s -fvisibility=hidden -m64 build ok! ``` Here we have added the `-v` parameter and looked at the detailed compile commands and detected mingw toolchain configuration values, where cross is automatically detected as:` x86_64-w64-mingw32-`, and the bin directory is also automatically detected , As well as compilers and linkers. Although it is not possible to automatically detect the sdk path on linux/win, we can also manually specify the sdk path. It should be noted that xmake specifically provides a `--mingw =` parameter for mingw to specify the tool chain root of mingw The directory has the same effect as `--sdk =`, but it can be set as a global configuration. ```sh $ xmake g --mingw=/home/mingwsdk $ xmake f -p mingw $ xmake ``` After setting the `--mingw` root directory to the global configuration through the` xmake g/global` command, after each compilation and switching of the compilation platform, there is no need to specify an additional mingw toolchain path, which is convenient for use. In addition, the usage of other tool chain configuration parameters is the same as that described above. For example, `--cross`,` --bin=`, etc. can be adjusted according to the actual needs of the environment. Own mingw tool chain. xmake also supports the llvm-mingw tool chain, which can be switched to arm/arm64 architecture to compile. ```sh $ xmake f --mingw=/xxx/llvm-mingw -a arm64 $ xmake ``` ### LLVM Toolchain The tool chain of llvm is relatively standard, only need to set the sdk configuration path to use: ```sh $ xmake f -p cross --toolchain=llvm --sdk="C:\Program Files\LLVM" $ xmake ``` ### GNU-RM Toolchain toolchain downlaod url: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads# ```sh $ xmake f -p cross --toolchain=gnu-rm --sdk=/xxx/cc-arm-none-eabi-9-2019-q4-major $ xmake ``` ### TinyC Toolchain ```sh $ xmake f --toolchain=tinyc $ xmake ``` ::: tip NOTE In the Releases directory, we also provide a special xmake-tinyc-vX.X.X.win32.exe installation package, built-in tinyc tool chain, without relying on msvc, you can also compile c code, out of the box use without dependencies. ::: ### Emcc tool chain Usually only need to switch to the Wasm platform, which has built-in emcc toolchain, and additionally adjusts the extension of the target program to `*.html` and output `*.wasm`. ```sh $ xmake f -p wasm $ xmake ``` However, we can also switch directly to the emcc toolchain, but the suffix name will not be modified. ```sh $ xmake f --toolchain=emcc $ xmake ``` ### Intel C++ Compiler Tool Chain ```sh $ xmake f --toolchain=icc $ xmake ``` ### Intel Fortran Compilation Tool Chain ```sh $ xmake f --toolchain=ifort $ xmake ``` ## Common Cross-compilation configuration 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. ```sh $ 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 ``` These are handled automatically by Xmake; there is no need to configure them manually. ### --bin * Set the `bin` directory of toolchains We need set it manually if the toolchains /bin directory is in other places, for example: ```sh $ xmake f -p linux --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin $ xmake ``` ::: tip NOTE 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: ```sh $ xmake f -p linux --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- ``` ### --as * Set `asm` assembler ```sh $ 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. ::: tip NOTE 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 ```sh $ 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. ::: tip NOTE 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 ```sh $ 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. ::: tip NOTE 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 ```sh $ 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. ::: tip NOTE 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 ```sh $ 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. ::: tip NOTE 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 ```sh $ 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. ::: tip NOTE We can set a unknown compiler as like-ar archiver, .e.g `xmake f --ar=ar@/home/xxx/armips.exe` ::: --- --- url: /api/description/custom-rule.md --- # 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. Custom build rules can have a set of file extensions associated to them using `set_extensions`. Once these extensions are associated to the rule a later call to `add_files` will automatically use this custom rule. Here is an example rule that will use Pandoc to convert markdown files added to a build target in to HTML files: ```lua -- Define a build rule for a markdown file rule("markdown") set_extensions(".md", ".markdown") on_build_file(function (target, sourcefile, opt) import("core.project.depend") import("utils.progress") -- it only for v2.5.9, we need use print to show progress below v2.5.8 -- make sure build directory exists os.mkdir(target:targetdir()) -- replace .md with .html local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- only rebuild the file if its changed since last run depend.on_changed(function () -- call pandoc to make a standalone html file from a markdown file os.vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) progress.show(opt.progress, "${color.build.object}markdown %s", sourcefile) end, {files = sourcefile}) end) target("test") set_kind("object") -- 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") ``` ::: tip NOTE Note that in xmake a rule is responsible for checking when targets are out of date and informing the user of ongoing progress. ::: There is also an alternative to `on_build_file` in the form of `on_build_files` which allows you to process the entire set of files in one function call. A second form called `on_buildcmd_file` and `on_buildcmd_files` is instead declarative; rather than running arbitrary Lua to build a target it runs Lua to learn how those targets are built. The advantage to `buildcmd` is that those rules can be exported to makefiles which do not require xmake at all in order to run. We can use buildcmd to simplify it further, like this: ```lua -- Define a build rule for a markdown file rule("markdown") set_extensions(".md", ".markdown") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) -- make sure build directory exists batchcmds:mkdir(target:targetdir()) -- replace .md with .html local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- call pandoc to make a standalone html file from a markdown file batchcmds:vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) batchcmds:show_progress(opt.progress, "${color.build.object}markdown %s", sourcefile) -- only rebuild the file if its changed since last run batchcmds:add_depfiles(sourcefile) end) target("test") set_kind("object") -- 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") ``` Files can be assigned to a specific rule regardless of their file extension. You do this by setting the `rule` custom property when adding the file like in the following example: ```lua target("test") add_files("src/test/*.md.in", {rules = "markdown"}) ``` A target can be superimposed to apply multiple rules to more customize its own build behavior, and even support different build environments. ::: tip NOTE Rules specified by `add_files("*.md", {rules = "markdown"})`, with a higher priority than the rule set by `add_rules("markdown")`. ::: ## rule * Defining rules #### Function Prototype ::: tip API ```lua rule(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Rule name string | #### Usage ```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) ``` ## add\_deps * Adding rule dependencies #### Function Prototype ::: tip API ```lua add_deps(deps: , ..., { order = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | deps | Dependency rule name string or array | | ... | Variable parameters, can pass multiple dependency names | | order | Whether to execute dependencies in order | #### Usage Associated dependencies can bind a batch of rules, i.e. instead of adding rules one by one to a target using `add_rules()`, just apply a rule that will take effect for it and all its dependencies. For example ```lua rule("foo") add_deps("bar") rule("bar") ... ``` We only need `add_rules("foo")` to apply both foo and bar rules. However, by default there is no order of execution between dependencies, and scripts such as `on_build_file` for foo and bar are executed in parallel, in an undefined order. To strictly control the order of execution, you can configure `add_deps("bar", {order = true})` to tell xmake that we need to execute scripts at the same level according to the order of dependencies. Example. ```lua rule("foo") add_deps("bar", {order = true}) on_build_file(function (target, sourcefile) end) rule("bar") on_build_file(function (target, sourcefile) end) ``` bar's `on_build_file` will be executed first. ::: tip NOTE To control the order of dependencies, we need xmake 2.7.2 or above to support this. ::: However, this way of controlling dependencies only works if both foo and bar rules are custom rules, and this does not work if you want to insert your own rules to be executed before xmake's built-in rules. In this case, we need to use a more flexible dynamic rule creation and injection approach to modify the built-in rules. For example, if we want to execute the `on_build_file` script for a custom cppfront rule before the built-in `c++.build` rule, we can do this in the following way. ```lua rule("cppfront") set_extensions(".cpp2") on_load(function (target) local rule = target:rule("c++.build"):clone() rule:add("deps", "cppfront", {order = true}) target:rule_add(rule) end) on_build_file(function (target, sourcefile, opt) print("build cppfront file") end) target("test") set_kind("binary") add_rules("cppfront") add_files("src/*.cpp") add_files("src/*.cpp2") ``` ## add\_imports * Add imported modules for all custom scripts #### Function Prototype ::: tip API ```lua add_imports(modules: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | modules | Module name string or array | | ... | Variable parameters, can pass multiple module names | #### Usage For usage and description, please see: [target:add\_imports](/api/description/project-target#add-imports), the usage is the same. ## set\_extensions * Setting the file extension type supported by the rule #### Function Prototype ::: tip API ```lua set_extensions(extensions: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | extensions | File extension string or array | | ... | Variable parameters, can pass multiple extensions | #### Usage 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") ``` ## on\_load * Custom load script #### Function Prototype ::: tip API ```lua on_load(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Load script function with target parameter | #### Usage 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", "TEST") end) ``` ## on\_config * custom configuration script #### Function Prototype ::: tip API ```lua on_config(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Configuration script function with target parameter | #### Usage After `xmake config` is executed, this script is executed before Build, which is usually used for configuration work before compilation. It differs from on\_load in that on\_load is executed as soon as the target is loaded, and the execution timing is earlier. If some configuration cannot be configured prematurely in on\_load, it can be configured in on\_config. In addition, its execution time is earlier than before\_build, and the approximate execution flow is as follows: ``` on_load -> after_load -> on_config -> before_build -> on_build -> after_build ``` ## on\_link * Custom link script #### Function Prototype ::: tip API ```lua on_link(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Link script function with target parameter | #### Usage 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) ``` ## on\_build * Custom compilation script #### Function Prototype ::: tip API ```lua on_build(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Build script function with target parameter | #### Usage 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) ``` ## on\_clean * Custom cleanup script #### Function Prototype ::: tip API ```lua on_clean(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Clean script function with target parameter | #### Usage 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) ``` ## on\_package * Custom packaging script #### Function Prototype ::: tip API ```lua on_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Package script function with target parameter | #### Usage 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) ``` ## on\_install * Custom installation script #### Function Prototype ::: tip API ```lua on_install(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Install script function with target parameter | #### Usage 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) ``` ## on\_uninstall * Custom Uninstall Script #### Function Prototype ::: tip API ```lua on_uninstall(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Uninstall script function with target parameter | #### Usage 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) ``` ## on\_build\_file * Customizing the build script to process one source file at a time #### Function Prototype ::: tip API ```lua on_build_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Build file script function with target, sourcefile and opt parameters | #### Usage ```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. ## on\_buildcmd\_file * Custom batch compile script, process one source file at a time #### Function Prototype ::: tip API ```lua on_buildcmd_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Build command file script function with target, batchcmds, sourcefile and opt parameters | #### Usage This is a new interface added in version 2.5.2. The script inside will not directly construct the source file, but will construct a batch command line task through the batchcmds object. When xmake actually executes the build, it executes these commands once. This is very useful for project generator plugins such as `xmake project`, because third-party project files generated by the generator do not support the execution of built-in scripts such as `on_build_files`. But the final result of `on_buildcmd_files` construction is a batch of original cmd command lines, which can be directly executed as custom commands for other project files. In addition, compared to `on_build_files`, it also simplifies the implementation of compiling extension files, is more readable and easy to configure, and is more user-friendly. ```lua rule("foo") set_extensions(".xxx") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}) batchcmds:add_depfiles("/xxxxx/dependfile.h", ...) -- batchcmds:add_depvalues(...) -- batchcmds:set_depmtime(os.mtime(...)) -- batchcmds:set_depcache("xxxx.d") end) ``` In addition to `batchcmds:vrunv`, we also support some other batch commands, such as: ```lua batchcmds:show("hello %s", "xmake") batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}, {envs = {LD_LIBRARY_PATH="/xxx"}}) batchcmds:mkdir("/xxx") - and cp, mv, rm, ln .. batchcmds:compile(sourcefile_cx, objectfile, {configs = {includedirs = sourcefile_dir, languages = (sourcekind == "cxx" and "c++11")}}) batchcmds:link(objectfiles, targetfile, {configs = {linkdirs = ""}}) ``` At the same time, we also simplify the configuration of dependency execution in it. The following is a complete example: ```lua rule("lex") set_extensions(".l", ".ll") on_buildcmd_file(function (target, batchcmds, sourcefile_lex, opt) - imports import("lib.detect.find_tool") - get lex local lex = assert(find_tool("flex") or find_tool("lex"), "lex not found!") - get c/c++ source file for lex local extension = path.extension(sourcefile_lex) local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c")) - add objectfile local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) - add commands batchcmds:show_progress(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex) batchcmds:mkdir(path.directory(sourcefile_cx)) batchcmds:vrunv(lex.program, {"-o", sourcefile_cx, sourcefile_lex}) batchcmds:compile(sourcefile_cx, objectfile) - add deps batchcmds:add_depfiles(sourcefile_lex) batchcmds:set_depmtime(os.mtime(objectfile)) batchcmds:set_depcache(target:dependfile(objectfile)) end) ``` For a detailed description and background of this, see: [issue 1246](https://github.com/xmake-io/xmake/issues/1246) ## on\_build\_files * Customizing the build script to process multiple source files at once #### Function Prototype ::: tip API ```lua on_build_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Build files script function with target, sourcebatch and opt parameters | #### Usage 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) ``` ## on\_buildcmd\_files * Customize batch compiling script, process multiple source files at once #### Function Prototype ::: tip API ```lua on_buildcmd_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Build command files script function with target, batchcmds, sourcebatch and opt parameters | #### Usage For a detailed description of this, see: [on\_buildcmd\_file](#on_buildcmd_file) ```lua rule("foo") set_extensions(".xxx") on_buildcmd_files(function (target, batchcmds, sourcebatch, opt) for _, sourcefile in ipairs(sourcebatch.sourcefiles) do batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}) end end) ``` ## before\_config * Custom pre-configuration script #### Function Prototype ::: tip API ```lua before_config(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before config script function with target parameter | #### Usage Used to implement the execution script before custom target configuration, for example: ```lua rule("test") before_config(function (target) end) ``` It will be executed before on\_config. ## before\_link * Custom pre-link script #### Function Prototype ::: tip API ```lua before_link(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before link script function with target parameter | #### Usage Execution scripts used to implement custom target links, for example: ```lua rule("test") before_link(function (target) end) ``` ## before\_build * Custom pre-compilation script #### Function Prototype ::: tip API ```lua before_build(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build script function with target parameter | #### Usage Used to implement the execution script before the custom target is built, for example: ```lua rule("markdown") before_build(function (target) end) ``` ## before\_clean * Custom pre-cleanup script #### Function Prototype ::: tip API ```lua before_clean(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before clean script function with target parameter | #### Usage Used to implement the execution script before the custom target cleanup, for example: ```lua rule("markdown") before_clean(function (target) end) ``` ## before\_package * Custom the pre-package script #### Function Prototype ::: tip API ```lua before_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before package script function with target parameter | #### Usage Used to implement the execution script before the custom target is packaged, for example: ```lua rule("markdown") before_package(function (target) end) ``` ## before\_install * Custom pre-installation script #### Function Prototype ::: tip API ```lua before_install(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before install script function with target parameter | #### Usage Used to implement the execution script before the custom target installation, for example: ```lua rule("markdown") before_install(function (target) end) ``` ## before\_uninstall * Custom pre-uninstall script #### Function Prototype ::: tip API ```lua before_uninstall(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before uninstall script function with target parameter | #### Usage Used to implement the execution script before the custom target is uninstalled, for example: ```lua rule("markdown") before_uninstall(function (target) end) ``` ## before\_build\_file * Custom pre-compilation script to process one source file at a time #### Function Prototype ::: tip API ```lua before_build_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build file script function with target, sourcefile and opt parameters | #### Usage Similar to [on\_build\_file](#on_build_file), but the timing of this interface is called before compiling a source file. Generally used to preprocess some source files before compiling. ## before\_buildcmd\_file * Customize the pre-compilation batch script, process one source file at a time #### Function Prototype ::: tip API ```lua before_buildcmd_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build command file script function with target, batchcmds, sourcefile and opt parameters | #### Usage Similar to the usage of [on\_buildcmd\_file](#on_buildcmd_file), but the time when this interface is called is before compiling a certain source file. It is generally used to pre-process certain source files before compilation. ## before\_build\_files * Customize pre-compilation scripts to process multiple source files at once #### Function Prototype ::: tip API ```lua before_build_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build files script function with target, sourcebatch and opt parameters | #### Usage Similar to the usage of [on\_build\_files](#on_build_files), but the time when this interface is called is before compiling some source files, It is generally used to pre-process certain source files before compilation. ## before\_buildcmd\_files * Customize the pre-compilation batch script to process multiple source files at once #### Function Prototype ::: tip API ```lua before_buildcmd_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build command files script function with target, batchcmds, sourcebatch and opt parameters | #### Usage Similar to the usage of [on\_buildcmd\_files](#on_buildcmd_files), but the time when this interface is called is before compiling some source files, It is generally used to pre-process certain source files before compilation. ## after\_config * Custom post-configuration script #### Function Prototype ::: tip API ```lua after_config(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After config script function with target parameter | #### Usage Used to implement the execution script after custom target configuration, for example: ```lua rule("test") after_config(function (target) end) ``` It will be executed after on\_config. ## after\_link * Custom post-linking script #### Function Prototype ::: tip API ```lua after_link(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After link script function with target parameter | #### Usage The execution script used to implement the custom target link is similar to [after\_link](#after_link). ## after\_build * Custom post-compilation script #### Function Prototype ::: tip API ```lua after_build(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build script function with target parameter | #### Usage The execution script used to implement the custom target build is similar to [before\_build](#before_build). ## after\_clean * Custom post-cleaning script #### Function Prototype ::: tip API ```lua after_clean(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After clean script function with target parameter | #### Usage The execution script used to implement the custom target cleanup is similar to [before\_clean](#before_clean). ## after\_package * Custom post-packaging script #### Function Prototype ::: tip API ```lua after_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After package script function with target parameter | #### Usage The execution script used to implement the custom target package is similar to [before\_package](#before_package). ## after\_install * Custom post-installation script #### Function Prototype ::: tip API ```lua after_install(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After install script function with target parameter | #### Usage The execution script used to implement the custom target installation is similar to [before\_install](#before_install). ## after\_uninstall * Custom post-uninstallation Script #### Function Prototype ::: tip API ```lua after_uninstall(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After uninstall script function with target parameter | #### Usage The execution script used to implement the custom target uninstallation is similar to [before\_uninstall](#before_uninstall). ## after\_build\_file * Custom post-compilation scripts to process one source file at a time #### Function Prototype ::: tip API ```lua after_build_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build file script function with target, sourcefile and opt parameters | #### Usage Similar to [on\_build\_file](#on_build_file), but the timing of this interface is called after compiling a source file. Generally used to post-process some compiled object files. ## after\_buildcmd\_file * Customize the compiled batch script, process one source file at a time #### Function Prototype ::: tip API ```lua after_buildcmd_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build command file script function with target, batchcmds, sourcefile and opt parameters | #### Usage Similar to the usage of [on\_buildcmd\_file](#on_buildcmd_file), but the time when this interface is called is after compiling a certain source file, Generally used for post-processing some compiled object files. ## after\_build\_files * Customize the compiled script to process multiple source files at once #### Function Prototype ::: tip API ```lua after_build_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build files script function with target, sourcebatch and opt parameters | #### Usage The usage is similar to [on\_build\_files](#on_build_files), but the time when this interface is called is after some source files are compiled, Generally used for post-processing some compiled object files. ## after\_buildcmd\_files * Customize the compiled batch script to process multiple source files at once #### Function Prototype ::: tip API ```lua after_buildcmd_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build command files script function with target, batchcmds, sourcebatch and opt parameters | #### Usage The usage is similar to [on\_buildcmd\_files](#on_buildcmd_files), but the time when this interface is called is after compiling some source files, Generally used for post-processing some compiled object files. ## rule\_end * End definition rules #### Function Prototype ::: tip API ```lua rule_end() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | - | No parameters | #### Usage This is optional. If you want to manually end the rule definition, you can call it: ```lua rule("test") -- .. rule_end() ``` --- --- url: /guide/project-configuration/custom-rule.md --- # Custom Rules {#custom-rule} Xmake not only natively supports multi-language file building, but also allows users to implement complex unknown file building through custom build rules. Custom rules let you define specialized build logic for specific file types. ## Basic Concepts {#basic-concepts} Custom build rules are defined using the `rule()` function and associate a set of file extensions to rules through `set_extensions()`. Once these extensions are associated with rules, calls to `add_files()` will automatically use this custom rule. ## Creating Simple Rules {#create-simple-rule} ### Basic Syntax ```lua rule("rulename") set_extensions(".ext1", ".ext2") on_build_file(function (target, sourcefile, opt) -- build logic end) ``` ### Example: Markdown to HTML ```lua -- Define a build rule for markdown files rule("markdown") set_extensions(".md", ".markdown") on_build_file(function (target, sourcefile, opt) import("core.project.depend") -- make sure build directory exists os.mkdir(target:targetdir()) -- replace .md with .html local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- only rebuild if file has changed depend.on_changed(function () -- call pandoc to convert markdown to html os.vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) end, {files = sourcefile}) end) target("test") set_kind("object") add_rules("markdown") add_files("src/*.md") ``` ## Applying Rules to Targets {#apply-rules-to-target} ### Method 1: Using add\_rules() ```lua target("test") set_kind("binary") add_rules("markdown") -- apply markdown rule add_files("src/*.md") -- automatically use markdown rule ``` ### Method 2: Specifying in add\_files ```lua target("test") set_kind("binary") add_files("src/*.md", {rules = "markdown"}) -- specify rule for specific files ``` ::: tip Note Rules specified via `add_files("*.md", {rules = "markdown"})` have higher priority than rules set via `add_rules("markdown")`. ::: ## Rule Lifecycle {#rule-lifecycle} Custom rules support the complete build lifecycle and can execute custom logic at different stages: ### Main Stages * **on\_load**: Executed when rule is loaded * **on\_config**: Executed after configuration is complete * **before\_build**: Executed before building * **on\_build**: Executed during building (overrides default build behavior) * **after\_build**: Executed after building * **on\_clean**: Executed during cleaning * **on\_package**: Executed during packaging * **on\_install**: Executed during installation ### Example: Complete Lifecycle ```lua rule("custom") set_extensions(".custom") on_load(function (target) -- configuration when rule is loaded target:add("defines", "CUSTOM_RULE") end) before_build(function (target) -- preparation work before building print("Preparing to build custom files...") end) on_build_file(function (target, sourcefile, opt) -- process individual source files print("Building file:", sourcefile) end) after_build(function (target) -- cleanup work after building print("Custom build completed") end) ``` ## File Processing Methods {#file-processing-methods} ### Single File Processing (on\_build\_file) ```lua rule("single") set_extensions(".single") on_build_file(function (target, sourcefile, opt) -- process single file local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".out") os.cp(sourcefile, targetfile) end) ``` ### Batch File Processing (on\_build\_files) ```lua rule("batch") set_extensions(".batch") on_build_files(function (target, sourcebatch, opt) -- batch process multiple files for _, sourcefile in ipairs(sourcebatch.sourcefiles) do print("Processing file:", sourcefile) end end) ``` ## Batch Command Mode {#batch-command-mode} Using `on_buildcmd_file` and `on_buildcmd_files` can generate batch commands instead of directly executing builds: ```lua rule("markdown") set_extensions(".md", ".markdown") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) -- ensure build directory exists batchcmds:mkdir(target:targetdir()) -- generate target file path local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- add pandoc command batchcmds:vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) -- add dependency files batchcmds:add_depfiles(sourcefile) end) ``` ## Rule Dependencies {#rule-dependencies} ### Adding Rule Dependencies ```lua rule("foo") add_deps("bar") -- foo depends on bar rule rule("bar") set_extensions(".bar") on_build_file(function (target, sourcefile, opt) -- bar rule build logic end) ``` ### Controlling Execution Order ```lua rule("foo") add_deps("bar", {order = true}) -- ensure bar executes before foo on_build_file(function (target, sourcefile, opt) -- foo rule build logic end) ``` ## Common Interfaces {#common-interfaces} ### Setting File Extensions ```lua rule("myrule") set_extensions(".ext1", ".ext2", ".ext3") ``` ### Adding Import Modules ```lua rule("myrule") add_imports("core.project.depend", "utils.progress") on_build_file(function (target, sourcefile, opt) -- can directly use depend and progress modules end) ``` ### Getting Build Information ```lua rule("myrule") on_build_file(function (target, sourcefile, opt) print("Target name:", target:name()) print("Source file:", sourcefile) print("Build progress:", opt.progress) print("Target directory:", target:targetdir()) end) ``` ## Practical Examples {#practical-examples} ### Example 1: Resource File Processing ```lua rule("resource") set_extensions(".rc", ".res") on_build_file(function (target, sourcefile, opt) import("core.project.depend") local targetfile = target:objectfile(sourcefile) depend.on_changed(function () os.vrunv("windres", {sourcefile, "-o", targetfile}) end, {files = sourcefile}) end) ``` ### Example 2: Protocol Buffer Compilation ```lua rule("protobuf") set_extensions(".proto") on_build_file(function (target, sourcefile, opt) import("core.project.depend") local targetfile = path.join(target:autogendir(), path.basename(sourcefile) .. ".pb.cc") depend.on_changed(function () os.vrunv("protoc", {"--cpp_out=" .. target:autogendir(), sourcefile}) end, {files = sourcefile}) -- add generated file to target target:add("files", targetfile) end) ``` ## Best Practices {#best-practices} 1. **Use Dependency Checking**: Avoid unnecessary rebuilds through `depend.on_changed()` 2. **Error Handling**: Add appropriate error handling logic in rules 3. **Progress Display**: Use `opt.progress` to display build progress 4. **Modularization**: Break complex rules into multiple simple rules 5. **Documentation**: Add clear comments and documentation for custom rules ## More Information {#more-information} * Complete API documentation: [Custom Rule API](/api/description/custom-rule) * Built-in rules reference: [Built-in Rules](/api/description/builtin-rules) * Rule examples: [Rule Examples](/examples/cpp/protobuf) --- --- url: /api/description/custom-toolchain.md --- # Custom Toolchain After version 2.3.4, xmake has supported custom toolchain in user's project xmake.lua, for example: ```lua -- define toolchain toolchain("myclang") -- mark as standalone toolchain set_kind("standalone") -- set toolset set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") add_defines("MYCLANG") -- check toolchain on_check(function (toolchain) return import("lib.detect.find_tool")("clang") end) -- on load on_load(function (toolchain) -- get march local march = is_arch("x86_64", "x64") and "-m64" or "-m32" -- init flags for c/c++ toolchain:add("cxflags", march) toolchain:add("ldflags", march) toolchain:add("shflags", march) if not is_plat("windows") and os.isdir("/usr") then for _, includedir in ipairs({"/usr/local/include", "/usr/include"}) do if os.isdir(includedir) then toolchain:add("includedirs", includedir) end end for _, linkdir in ipairs({"/usr/local/lib", "/usr/lib"}) do if os.isdir(linkdir) then toolchain:add("linkdirs", linkdir) end end end -- init flags for objc/c++ (with ldflags and shflags) toolchain:add("mxflags", march) -- init flags for asm toolchain:add("asflags", march) end) ``` Then use the following command to cut to the toolchain you defined: ```sh $ xmake f --toolchain=myclang ``` Of course, we can also switch to the specified target directly to the custom toolchain through the `set_toolchains` interface. Before customizing the tool, we can run the following command to view the complete list of built-in toolchains to ensure that xmake does not provide it. If so, just use it directly. There is no need to define it yourself: ```sh $ xmake show -l toolchains ``` ## toolchain * Define toolchain #### Function Prototype ::: tip API ```lua toolchain(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Toolchain name string | #### Usage It can be defined in the user project xmake.lua, or it can be independently defined by a separate xmake.lua to specifically define various toolchains ```lua toolchain("myclang")     set_toolset("cc", "clang")     set_toolset("cxx", "clang", "clang++") toolchain_end() ``` * Define cross toolchain We can also customize the configuration for different cross toolchain sdk in xmake.lua. Usually only need to specify sdkdir, xmake can automatically detect other configurations, such as cross and other information, for example: ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") toolchain_end() target("hello") set_kind("binary") add_files("apps/hello/*.c") ``` This is the most streamlined cross-toolchain configuration. It only sets the corresponding SDK path, and then marks it as a complete and independent toolchain by `set_kind("standalone")`. At this time, we can use the command line `--toolchain=my_toolchain` to manually switch to this toolchain. ```sh xmake f --toolchain=my_toolchain xmake ``` In addition, we can also directly bind it to the corresponding target through `set_toolchains` in xmake.lua, then only when this target is compiled, will we switch to our custom toolchain. ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") toolchain_end() target("hello") set_kind("binary") add_files("apps/hello/*.c") set_toolchains("my_toolchain") ``` In this way, we no longer need to switch the toolchain manually, just execute xmake, and it will automatically switch to the my\_toolchain toolchain by default. This is especially useful for embedded development, because there are many cross-compilation tool chains for embedded platforms, and we often need various switches to complete the compilation of different platforms. Therefore, we can place all toolchain definitions in a separate lua file to define, for example: ``` projectdir -xmake.lua -toolchains -my_toolchain1.lua -my_toolchain2.lua -... ``` Then, we only need to introduce them through includes in xmake.lua, and bind different tool chains according to different custom platforms: ```lua includes("toolchains/*.lua") target("hello") set_kind("binary") add_files("apps/hello/*.c") if is_plat("myplat1") then set_toolchains("my_toolchain1") elseif is_plat("myplat2") then set_toolchains("my_toolchain2") end ``` In this way, we can quickly switch the designated platform directly when compiling to automatically switch the corresponding tool chain. ```sh xmake f -p myplat1 xmake ``` If some cross-compilation toolchains are complex in structure and automatic detection is not enough, you can use `set_toolset`, `set_cross` and `set_bindir` interfaces according to the actual situation to configure other settings in a targeted manner. For example, in the following example, we also added some cxflags/ldflags and the built-in system library links. ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") on_load(function (toolchain) - add flags for arch if toolchain:is_arch("arm") then toolchain:add("cxflags", "-march=armv7-a", "-msoft-float", {force = true}) toolchain:add("ldflags", "-march=armv7-a", "-msoft-float", {force = true}) end toolchain:add("ldflags", "--static", {force = true}) toolchain:add("syslinks", "gcc", "c") end) ``` For more examples of custom toolchains, we can see the following interface documents, or refer to the built-in toolchain definition in the directory of xmake source code: \[Internal Toolchain List]\(https://github.com/xmake-io /xmake/blob/master/xmake/toolchains/) ## set\_kind * Set toolchain type #### Function Prototype ::: tip API ```lua set_kind(kind: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | kind | Toolchain type: "standalone" | #### Usage Currently only supports the setting of `standalone` type, which means that the current toolchain is an independent and complete toolchain, including a complete set of tool set configurations such as cc/cxx/ld/sh/ar and other compilers, archivers, and linkers. Usually used when a target is set with multiple toolchains at the same time, but only one independent toolchain can be effective at the same time. This configuration can ensure that the toolchains in effect are mutually exclusive. For example, the gcc/clang toolchain will not be simultaneously. Take effect. However, local toolchains such as yasm/nasm belong to the extension of additional local toolchains, and there is no need to set up standalone because two toolchains of clang/yasm may exist at the same time. ::: tip NOTE Just remember that the toolchain with a complete compilation environment is set to standalone ::: ## set\_toolset * Set Tool Set #### Function Prototype ::: tip API ```lua set_toolset(tool: , tools: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tool | Tool name string (cc, cxx, ld, sh, ar, ex, strip, mm, mxx, as) | | tools | Tool program name string or array | | ... | Variable parameters, can pass multiple tool names | #### Usage Used to set the name and path of each individual tool, for example: ```lua toolchain("myclang")     set_kind("standalone")     set_toolset("cc", "clang")     set_toolset("cxx", "clang", "clang++")     set_toolset("ld", "clang++", "clang")     set_toolset("sh", "clang++", "clang")     set_toolset("ar", "ar")     set_toolset("ex", "ar")     set_toolset("strip", "strip")     set_toolset("mm", "clang")     set_toolset("mxx", "clang", "clang++")     set_toolset("as", "clang") ``` If you provide multiple tool options, they will be searched in order. For example, the following will attempt to find `clang` and will then try to use `gcc` if `clang` cannot be found: ```lua     set_toolset("cc", "clang", "gcc") ``` If your tool has the same name as a supported tool but a different name, you can specify this as `{generic tool name}@{tool name}`. For example the following will look for a C compiler called `clang-mytarget` suffix but will then assume that the tool behaves like clang: ```lua     set_toolset("cc", "clang@clang-mytarget") ``` For details about this interface, you can see: [target.set\_toolset](/api/description/project-target.html#set-toolset) ## set\_sdkdir * Set toolchain sdk directory path #### Function Prototype ::: tip API ```lua set_sdkdir(sdkdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sdkdir | SDK directory path string | #### Usage Usually we can configure the sdk directory through `xmake f --toolchain=myclang --sdk=xxx`, but each time the configuration is more cumbersome, we can also pre-configure to xmake.lua through this interface to facilitate quick switching. ```lua toolchain("myclang")     set_kind("standalone")     set_sdkdir("/tmp/sdkdir")     set_toolset("cc", "clang") ``` ## set\_bindir * Set toolchain bin directory path #### Function Prototype ::: tip API ```lua set_bindir(bindir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | bindir | Binary directory path string | #### Usage Normally, we can configure the SDK directory through `xmake f --toolchain=myclang --bin=xxx`, but each time the configuration is more cumbersome, we can also pre-configure to xmake.lua through this interface, which is convenient for quick switching. ```lua toolchain("myclang")     set_kind("standalone")     set_bindir("/tmp/sdkdir/bin")     set_toolset("cc", "clang") ``` ## on\_check * Detection toolchain #### Function Prototype ::: tip API ```lua on_check(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Check script function with toolchain parameter | #### Usage It is used to detect whether the sdk or program where the specified toolchain exists exists on the current system. It is usually used in the case of multiple standalone toolchains to automatically detect and select an effective toolchain. For scenes specified manually by `xmake f --toolchain=myclang`, this detection configuration is not necessary and can be omitted. ```lua toolchain("myclang")     on_check(function (toolchain)         return import("lib.detect.find_tool")("clang")     end) ``` ## on\_load * Load toolchain #### Function Prototype ::: tip API ```lua on_load(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Load script function with toolchain parameter | #### Usage For some complex scenarios, we can dynamically and flexibly set various toolchain configurations in on\_load, which is more flexible and powerful than setting in the description field: ```lua toolchain("myclang") set_kind("standalone") on_load(function (toolchain) -- set toolset toolchain:set("toolset", "cc", "clang") toolchain:set("toolset", "ld", "clang++") -- init flags local march = toolchain:is_arch("x86_64", "x64") and "-m64" or "-m32" toolchain:add("cxflags", march) toolchain:add("ldflags", march) toolchain:add("shflags", march) end) ``` ## toolchain\_end * End definition toolchain #### Function Prototype ::: tip API ```lua toolchain_end() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | - | No parameters | #### Usage This is optional, if you want to manually end the definition of toolchain, you can call it: ```lua toolchain("myclang")     - .. toolchain_end() ``` --- --- url: /examples/other-languages/rust.md --- Create an empty project: ```sh $ xmake create -l rust -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/main.rs") ``` For more examples, see: [Rust Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/rust) ## Add cargo package dependences example: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cargo\_deps ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` ## Integrating Cargo.toml dependency packages Integrating dependencies directly using `add_requires("cargo::base64 0.13.0")` above has a problem. If there are a lot of dependencies and several dependencies all depend on the same child dependencies, then there will be a redefinition problem, so if we use the full Cargo.toml to manage the dependencies we won't have this problem. For example ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::test", {configs = {cargo_toml = path.join(os.projectdir(), "Cargo.toml")}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::test") ``` For a complete example see: [cargo\_deps\_with\_toml](https://github.com/xmake-io/xmake/blob/dev/tests/projects/rust/cargo_deps_with_toml/xmake.lua) ## Use cxxbridge to call rust in c++ example: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cxx\_call\_rust\_library ```lua add_rules("mode.debug", "mode.release") add_requires("cargo::cxx 1.0") target("foo") set_kind("static") add_files("src/foo.rs") set_values("rust.cratetype", "staticlib") add_packages("cargo::cxx") target("test") set_kind("binary") add_rules("rust.cxxbridge") add_deps("foo") add_files("src/main.cc") add_files("src/bridge.rsx") ``` ```rust [foo.rs] #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } pub fn add(a: i32, b: i32) -> i32 { return a + b; } ``` bridge interface file in c++, bridge.rsx ```rust #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } ``` ```c++ [main.cc] #include #include "bridge.rs.h" int main(int argc, char** argv) { printf("add(1, 2) == %d\n", add(1, 2)); return 0; } ``` ## Call c++ in rust example: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/rust\_call\_cxx\_library ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.cc") target("test") set_kind("binary") add_deps("foo") add_files("src/main.rs") ``` ```rust [main.rs] extern "C" { fn add(a: i32, b: i32) -> i32; } fn main() { unsafe { println!("add(1, 2) = {}", add(1, 2)); } } ``` ```c++ [foo.cc] extern "C" int add(int a, int b) { return a + b; } ``` --- --- url: /zh/examples/other-languages/rust.md --- 创建空工程: ```sh $ xmake create -l rust -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/main.rs") ``` 更多例子见:[Rust Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/rust) ## 添加 Cargo 包依赖 {#cargo-deps} 例子: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cargo\_deps ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` ## 集成 Cargo.toml 的依赖包 {#cargo-toml} 上面直接使用 `add_requires("cargo::base64 0.13.0")` 的方式集成依赖,会有一个问题: 如果依赖很多,并且有几个依赖都共同依赖了相同的子依赖,那么会出现重定义问题,因此如果我们使用完整的 Cargo.toml 去管理依赖就不会存在这个问题。 例如: ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::test", {configs = {cargo_toml = path.join(os.projectdir(), "Cargo.toml")}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::test") ``` 完整例子见:[cargo\_deps\_with\_toml](https://github.com/xmake-io/xmake/blob/dev/tests/projects/rust/cargo_deps_with_toml/xmake.lua) ## 使用 cxxbridge 在 c++ 中调用 rust {#call-rust-in-cxx} 例子: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cxx\_call\_rust\_library ```lua add_rules("mode.debug", "mode.release") add_requires("cargo::cxx 1.0") target("foo") set_kind("static") add_files("src/foo.rs") set_values("rust.cratetype", "staticlib") add_packages("cargo::cxx") target("test") set_kind("binary") add_rules("rust.cxxbridge") add_deps("foo") add_files("src/main.cc") add_files("src/bridge.rsx") ``` ```rust [foo.rs] #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } pub fn add(a: i32, b: i32) -> i32 { return a + b; } ``` 我们还需要在 c++ 项目中添加桥接文件 bridge.rsx ```rust #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } ``` ```c++ [main.cc] #include #include "bridge.rs.h" int main(int argc, char** argv) { printf("add(1, 2) == %d\n", add(1, 2)); return 0; } ``` ## 在 Rust 中调用 C++ {#call-cxx-in-rust} 例子: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/rust\_call\_cxx\_library ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.cc") target("test") set_kind("binary") add_deps("foo") add_files("src/main.rs") ``` ```rust [main.rs] extern "C" { fn add(a: i32, b: i32) -> i32; } fn main() { unsafe { println!("add(1, 2) = {}", add(1, 2)); } } ``` ```c++ [foo.cc] extern "C" int add(int a, int b) { return a + b; } ``` --- --- url: /guide/project-configuration/define-options.md --- # Define options {#define-option} ## Customize command line options We can define an option switch to control the internal configuration logic, for example: ```lua option("tests", {default = false, description = "Enable Tests"}) target("foo") set_kind("binary") add_files("src/*.cpp") if has_config("tests") then add_defines("TESTS") end ``` Then, we can enable this custom option in the command line, so that the macro definition of `-DTESTS` is automatically added when the foo target is compiled. ```sh $ xmake f --tests=y $ xmake ``` The above option configuration is relatively simple, so we use a single-line simplified writing method, and we can also use the complete domain configuration writing method. ```lua option("tests") set_default(false) set_description("Enable Tests") ``` ## Bind options to targets We can also use `add_options` to bind options to a specified target directly without using `has_config` and `add_defines` to set them manually. In this way, when the option is enabled, all associated settings in the tests option will be automatically set to the bound target. ```lua option("tests") set_description("Enable Tests") set_default(false) add_defines("TEST") target("foo") set_kind("binary") add_files("src/*.cpp") add_options("tests") ``` In the above example, when tests is enabled, the foo target will automatically be added with `-DTEST`. ## Option Types and Common APIs {#option-types-apis} ### Option Types * **Boolean**: Switch option, usually for enabling/disabling features * **String**: For paths, patterns, or custom values * **Multi-value**: Use `set_values` to provide choices (menu) ### Common APIs * `set_default(value)`: Set default value (bool or string) * `set_showmenu(true/false)`: Show in `xmake f --menu` * `set_description("desc")`: Set description * `set_values("a", "b", "c")`: Set selectable values (menu) * `add_defines("FOO")`: Add macro when enabled * `add_links("bar")`: Add link library when enabled * `add_cflags("-O2")`: Add compile flag when enabled ## Option Dependency and Conditional Control {#option-deps} * `add_deps("otheropt")`: Depend on other options, often used with on\_check/after\_check * `before_check`/`on_check`/`after_check`: Custom check logic, can enable/disable options dynamically #### Dependency Example ```lua option("feature_x") set_default(false) on_check(function (option) if has_config("feature_y") then option:enable(false) end end) ``` ## Option Instance APIs {#option-instance} In `on_check`, `after_check`, etc., you can use the option instance APIs: * `option:name()` get option name * `option:value()` get option value * `option:enable(true/false)` enable/disable option * `option:enabled()` check if enabled * `option:get("defines")` get config value * `option:set("defines", "FOO")` set config value * `option:add("links", "bar")` append config value ## Option and Target Integration {#option-in-target} * Use `add_options("opt1", "opt2")` to bind options to targets * When enabled, related configs are automatically applied to the target * You can also use `has_config("opt")` for conditional logic in target scope ## Typical Examples {#option-examples} ### 1. Boolean Switch Option ```lua option("enable_lto") set_default(false) set_showmenu(true) set_description("Enable LTO optimization") add_cflags("-flto") target("foo") add_options("enable_lto") ``` ### 2. Path/String Option ```lua option("rootdir") set_default("/tmp/") set_showmenu(true) set_description("Set root directory") target("foo") add_files("$(rootdir)/*.c") ``` ### 3. Multi-value Menu Option ```lua option("arch") set_default("x86_64") set_showmenu(true) set_description("Select architecture") set_values("x86_64", "arm64", "mips") ``` ## More Information {#more-information} * Complete API documentation: [Option API](/api/description/configuration-option) * Option instance APIs: [option instance API](/api/scripts/option-instance) --- --- url: /api/scripts/extension-modules/devel/git.md --- # devel.git This interface provides access to various git commands. Compared to directly calling git commands, this module provides more high-level and easy-to-use encapsulated interfaces, and provides automatic git detection and cross-platform processing. This is an extension module of xmake. ::: tip Tip To use this module, you need to import it first: `import("devel.git")` ::: ## git.clone * Clone repository #### Function Prototype ::: tip API ```lua git.clone(url: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Required. Git repository URL | | opt | Optional. Option parameters, supports the following:- `depth` - Shallow clone depth- `branch` - Specify branch- `outputdir` - Output directory- `recursive` - Recursively clone submodules- `longpaths` - Enable long path support- `treeless` - Use tree filter- `autocrlf` - Auto convert line endings- `fsmonitor` - Enable filesystem monitoring- `checkout` - Whether to perform checkout | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git clone` command, supporting options like shallow cloning, branch selection, and submodule handling. ```lua import("devel.git") -- Basic clone git.clone("git@github.com:xmake-io/xmake.git") -- Clone with options git.clone("git@github.com:xmake-io/xmake.git", { depth = 1, -- Shallow clone depth branch = "master", -- Specify branch outputdir = "/tmp/xmake", -- Output directory recursive = true, -- Recursively clone submodules longpaths = true, -- Enable long path support (Windows) treeless = true, -- Use tree filter (Git 2.16+) autocrlf = true, -- Auto convert line endings fsmonitor = true, -- Enable filesystem monitoring checkout = false -- Don't perform checkout }) ``` ## git.init * Initialize git repository #### Function Prototype ::: tip API ```lua git.init(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git init` command, used to create a new git repository in the current directory or specified directory. ```lua import("devel.git") -- Initialize in current directory git.init() -- Initialize in specified directory git.init({repodir = "/tmp/new_project"}) ``` ## git.pull * Pull latest commits from remote repository #### Function Prototype ::: tip API ```lua git.pull(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `remote` - Remote repository name- `branch` - Branch name- `tags` - Whether to pull tags- `force` - Whether to force pull- `repodir` - Repository directory- `fsmonitor` - Enable filesystem monitoring | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git pull` command, supporting options for specifying remote repository, branch, and tags. ```lua import("devel.git") -- Basic pull git.pull() -- Pull with options git.pull({ remote = "origin", -- Remote repository name branch = "master", -- Branch name tags = true, -- Pull tags force = true, -- Force pull repodir = "/tmp/xmake", -- Repository directory fsmonitor = true -- Enable filesystem monitoring }) ``` ## git.push * Push to remote repository #### Function Prototype ::: tip API ```lua git.push(remote: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | remote | Required. Remote repository URL | | opt | Optional. Option parameters, supports the following:- `branch` - Local branch- `remote_branch` - Remote branch- `force` - Whether to force push- `repodir` - Repository directory- `verbose` - Whether to output verbosely | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git push` command, supporting force push and specifying remote branches. ```lua import("devel.git") -- Push to remote repository git.push("https://github.com/user/repo.git", { branch = "master", -- Local branch remote_branch = "main", -- Remote branch force = true, -- Force push repodir = "/tmp/xmake", -- Repository directory verbose = true -- Verbose output }) ``` ## git.checkout * Checkout specified branch or commit #### Function Prototype ::: tip API ```lua git.checkout(ref: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ref | Required. Branch name, tag or commit hash | | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git checkout` command, used to switch branches or checkout specific commits. ```lua import("devel.git") -- Switch to branch git.checkout("master", {repodir = "/tmp/xmake"}) -- Checkout tag git.checkout("v1.0.1", {repodir = "/tmp/xmake"}) -- Checkout commit git.checkout("abc1234", {repodir = "/tmp/xmake"}) ``` ## git.reset * Reset repository state #### Function Prototype ::: tip API ```lua git.reset(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory- `hard` - Whether to hard reset- `soft` - Whether to soft reset- `commit` - Reset to specified commit | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git reset` command, supporting soft reset, hard reset and other options. ```lua import("devel.git") -- Basic reset git.reset({repodir = "/tmp/xmake"}) -- Hard reset to specified commit git.reset({ repodir = "/tmp/xmake", hard = true, -- Hard reset commit = "HEAD~1" -- Reset to previous commit }) -- Soft reset git.reset({ repodir = "/tmp/xmake", soft = true, -- Soft reset commit = "abc1234" -- Reset to specified commit }) ``` ## git.clean * Clean working directory #### Function Prototype ::: tip API ```lua git.clean(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory- `force` - Whether to force delete- `all` - Whether to delete all untracked files | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git clean` command, used to remove untracked files and directories. ```lua import("devel.git") -- Basic clean git.clean({repodir = "/tmp/xmake"}) -- Force clean git.clean({ repodir = "/tmp/xmake", force = true, -- Force delete all = true -- Delete all untracked files }) ``` ## git.apply * Apply patch file #### Function Prototype ::: tip API ```lua git.apply(patch: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | patch | Required. Patch file path | | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory- `reverse` - Whether to apply in reverse- `gitdir` - Git directory | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git apply` command, used to apply diff format patch files. ```lua import("devel.git") -- Apply patch git.apply("fix.patch", { repodir = "/tmp/xmake", reverse = true, -- Apply patch in reverse gitdir = ".git" -- Specify git directory }) -- Apply diff file git.apply("changes.diff") ``` ## git.branch * Get current branch name #### Function Prototype ::: tip API ```lua git.branch(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory | #### Return Value | Type | Description | |------|-------------| | string | Returns current branch name | #### Usage This interface corresponds to the `git branch --show-current` command, returns the name of the current branch. ```lua import("devel.git") -- Get current branch local branch = git.branch({repodir = "/tmp/xmake"}) print("Current branch:", branch) ``` ## git.lastcommit * Get latest commit hash #### Function Prototype ::: tip API ```lua git.lastcommit(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory | #### Return Value | Type | Description | |------|-------------| | string | Returns latest commit hash | #### Usage This interface corresponds to the `git rev-parse HEAD` command, returns the latest commit hash of the repository. ```lua import("devel.git") -- Get latest commit hash local commit = git.lastcommit({repodir = "/tmp/xmake"}) print("Last commit:", commit) ``` ## git.refs * Get all reference list #### Function Prototype ::: tip API ```lua git.refs(url: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Required. Remote repository URL | #### Return Value | Type | Description | |------|-------------| | table | Returns reference list array | #### Usage This interface corresponds to the `git ls-remote --refs` command, returns all references of the remote repository. ```lua import("devel.git") -- Get all references local refs = git.refs("https://github.com/xmake-io/xmake.git") for _, ref in ipairs(refs) do print("Ref:", ref) end ``` ## git.tags * Get all tag list #### Function Prototype ::: tip API ```lua git.tags(url: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Required. Remote repository URL | #### Return Value | Type | Description | |------|-------------| | table | Returns tag list array | #### Usage This interface corresponds to the `git ls-remote --tags` command, returns all tags of the remote repository. ```lua import("devel.git") -- Get all tags local tags = git.tags("https://github.com/xmake-io/xmake.git") for _, tag in ipairs(tags) do print("Tag:", tag) end ``` ## git.branches * Get all branch list #### Function Prototype ::: tip API ```lua git.branches(url: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Required. Remote repository URL | #### Return Value | Type | Description | |------|-------------| | table | Returns branch list array | #### Usage This interface corresponds to the `git ls-remote --heads` command, returns all branches of the remote repository. ```lua import("devel.git") -- Get all branches local branches = git.branches("https://github.com/xmake-io/xmake.git") for _, branch in ipairs(branches) do print("Branch:", branch) end ``` ## git.ls\_remote * Get remote reference information #### Function Prototype ::: tip API ```lua git.ls_remote(refs: , url: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | refs | Required. Reference type, e.g., "tags", "heads", "refs" | | url | Required. Remote repository URL | #### Return Value | Type | Description | |------|-------------| | table | Returns reference list array | #### Usage This interface corresponds to the `git ls-remote` command, supports getting tags, branches or all references. ```lua import("devel.git") -- Get tags local tags = git.ls_remote("tags", "https://github.com/xmake-io/xmake.git") -- Get branches local heads = git.ls_remote("heads", "https://github.com/xmake-io/xmake.git") -- Get all references local refs = git.ls_remote("refs") ``` ## git.checkurl * Check if it's a git URL #### Function Prototype ::: tip API ```lua git.checkurl(url: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Required. URL to check | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if it's a git URL, false otherwise | #### Usage Check if the given URL is a valid git repository address. ```lua import("devel.git") -- Check URL local is_git = git.checkurl("https://github.com/xmake-io/xmake.git") if is_git then print("This is a git URL") end ``` ## git.asgiturl * Convert URL to git format #### Function Prototype ::: tip API ```lua git.asgiturl(url: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Required. URL to convert | #### Return Value | Type | Description | |------|-------------| | string | Returns standard git URL format | #### Usage Convert various formats of URLs to standard git URL format, supporting custom protocols. ```lua import("devel.git") -- Convert URL to git format local git_url = git.asgiturl("github:xmake-io/xmake") print("Git URL:", git_url) -- https://github.com/xmake-io/xmake.git -- Supported custom protocols local protocols = { "github:user/repo", -- https://github.com/user/repo.git "gitlab:user/repo", -- https://gitlab.com/user/repo.git "gitee:user/repo", -- https://gitee.com/user/repo.git "bitbucket:user/repo" -- https://bitbucket.org/user/repo.git } ``` ## submodule.update * Update submodules #### Function Prototype ::: tip API ```lua submodule.update(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory- `init` - Whether to initialize submodules- `remote` - Whether to update remote information- `recursive` - Whether to recursively update- `force` - Whether to force update- `checkout` - Whether to checkout submodules- `merge` - Whether to merge mode- `rebase` - Whether to rebase mode- `reference` - Reference repository- `paths` - Specify submodule paths- `longpaths` - Enable long path support | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git submodule update` command, used to update repository submodules. ```lua import("devel.git.submodule") -- Basic update submodule.update({repodir = "/tmp/xmake"}) -- Update with options submodule.update({ repodir = "/tmp/xmake", init = true, -- Initialize submodules remote = true, -- Update remote information recursive = true, -- Recursive update force = true, -- Force update checkout = true, -- Checkout submodules merge = true, -- Merge mode rebase = true, -- Rebase mode reference = "/path/to/repo", -- Reference repository paths = {"submodule1", "submodule2"}, -- Specify submodule paths longpaths = true -- Enable long path support }) ``` ## submodule.clean * Clean submodules #### Function Prototype ::: tip API ```lua submodule.clean(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory- `force` - Whether to force delete- `all` - Whether to delete all untracked files | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git submodule foreach git clean` command, used to clean untracked files in all submodules. ```lua import("devel.git.submodule") -- Basic clean submodule.clean({repodir = "/tmp/xmake"}) -- Force clean submodule.clean({ repodir = "/tmp/xmake", force = true, -- Force delete all = true -- Delete all untracked files }) ``` ## submodule.reset * Reset submodules #### Function Prototype ::: tip API ```lua submodule.reset(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `repodir` - Repository directory- `hard` - Whether to hard reset- `soft` - Whether to soft reset- `commit` - Reset to specified commit- `longpaths` - Enable long path support | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface corresponds to the `git submodule foreach git reset` command, used to reset the state of all submodules. ```lua import("devel.git.submodule") -- Basic reset submodule.reset({repodir = "/tmp/xmake"}) -- Hard reset submodule.reset({ repodir = "/tmp/xmake", hard = true, -- Hard reset commit = "HEAD" -- Reset to specified commit }) -- Soft reset submodule.reset({ repodir = "/tmp/xmake", soft = true, -- Soft reset longpaths = true -- Enable long path support }) ``` --- --- url: /zh/api/scripts/extension-modules/devel/git.md --- # devel.git 此接口提供了git各种命令的访问接口,相对于直接调用git命令,此模块提供了更加上层易用的封装接口,并且提供对git的自动检测和跨平台处理。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("devel.git")` ::: ## git.clone * 克隆代码库 #### 函数原型 ::: tip API ```lua git.clone(url: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 必需。git仓库URL | | opt | 可选。选项参数,支持以下选项:- `depth` - 浅克隆深度- `branch` - 指定分支- `outputdir` - 输出目录- `recursive` - 递归克隆子模块- `longpaths` - 启用长路径支持- `treeless` - 使用树形过滤- `autocrlf` - 自动转换换行符- `fsmonitor` - 启用文件系统监控- `checkout` - 是否执行检出 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 克隆成功返回 true,失败返回 false | #### 用法说明 此接口对应`git clone`命令,支持深度克隆、分支选择、子模块处理等选项。 ```lua import("devel.git") -- 基本克隆 git.clone("git@github.com:xmake-io/xmake.git") -- 带选项的克隆 git.clone("git@github.com:xmake-io/xmake.git", { depth = 1, -- 浅克隆深度 branch = "master", -- 指定分支 outputdir = "/tmp/xmake", -- 输出目录 recursive = true, -- 递归克隆子模块 longpaths = true, -- 启用长路径支持(Windows) treeless = true, -- 使用树形过滤(Git 2.16+) autocrlf = true, -- 自动转换换行符 fsmonitor = true, -- 启用文件系统监控 checkout = false -- 不执行检出 }) ``` ## git.init * 初始化git仓库 #### 函数原型 ::: tip API ```lua git.init(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 初始化成功返回 true,失败返回 false | #### 用法说明 此接口对应`git init`命令,用于在当前目录或指定目录创建新的git仓库。 ```lua import("devel.git") -- 在当前目录初始化 git.init() -- 在指定目录初始化 git.init({repodir = "/tmp/new_project"}) ``` ## git.pull * 拉取远程仓库最新提交 #### 函数原型 ::: tip API ```lua git.pull(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `remote` - 远程仓库名- `branch` - 分支名- `tags` - 是否拉取标签- `force` - 是否强制拉取- `repodir` - 仓库目录- `fsmonitor` - 启用文件系统监控 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 拉取成功返回 true,失败返回 false | #### 用法说明 此接口对应`git pull`命令,支持指定远程仓库、分支和标签选项。 ```lua import("devel.git") -- 基本拉取 git.pull() -- 带选项的拉取 git.pull({ remote = "origin", -- 远程仓库名 branch = "master", -- 分支名 tags = true, -- 拉取标签 force = true, -- 强制拉取 repodir = "/tmp/xmake", -- 仓库目录 fsmonitor = true -- 启用文件系统监控 }) ``` ## git.push * 推送到远程仓库 #### 函数原型 ::: tip API ```lua git.push(remote: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | remote | 必需。远程仓库URL | | opt | 可选。选项参数,支持以下选项:- `branch` - 本地分支- `remote_branch` - 远程分支- `force` - 是否强制推送- `repodir` - 仓库目录- `verbose` - 是否详细输出 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 推送成功返回 true,失败返回 false | #### 用法说明 此接口对应`git push`命令,支持强制推送和指定远程分支。 ```lua import("devel.git") -- 推送到远程仓库 git.push("https://github.com/user/repo.git", { branch = "master", -- 本地分支 remote_branch = "main", -- 远程分支 force = true, -- 强制推送 repodir = "/tmp/xmake", -- 仓库目录 verbose = true -- 详细输出 }) ``` ## git.checkout * 签出指定分支或提交 #### 函数原型 ::: tip API ```lua git.checkout(ref: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ref | 必需。分支名、标签或提交哈希 | | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 签出成功返回 true,失败返回 false | #### 用法说明 此接口对应`git checkout`命令,用于切换分支或检出特定提交。 ```lua import("devel.git") -- 切换到分支 git.checkout("master", {repodir = "/tmp/xmake"}) -- 检出标签 git.checkout("v1.0.1", {repodir = "/tmp/xmake"}) -- 检出提交 git.checkout("abc1234", {repodir = "/tmp/xmake"}) ``` ## git.reset * 重置仓库状态 #### 函数原型 ::: tip API ```lua git.reset(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录- `hard` - 是否硬重置- `soft` - 是否软重置- `commit` - 重置到指定提交 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 重置成功返回 true,失败返回 false | #### 用法说明 此接口对应`git reset`命令,支持软重置、硬重置等选项。 ```lua import("devel.git") -- 基本重置 git.reset({repodir = "/tmp/xmake"}) -- 硬重置到指定提交 git.reset({ repodir = "/tmp/xmake", hard = true, -- 硬重置 commit = "HEAD~1" -- 重置到上一个提交 }) -- 软重置 git.reset({ repodir = "/tmp/xmake", soft = true, -- 软重置 commit = "abc1234" -- 重置到指定提交 }) ``` ## git.clean * 清理工作目录 #### 函数原型 ::: tip API ```lua git.clean(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录- `force` - 是否强制删除- `all` - 是否删除所有未跟踪文件 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 清理成功返回 true,失败返回 false | #### 用法说明 此接口对应`git clean`命令,用于移除未跟踪的文件和目录。 ```lua import("devel.git") -- 基本清理 git.clean({repodir = "/tmp/xmake"}) -- 强制清理 git.clean({ repodir = "/tmp/xmake", force = true, -- 强制删除 all = true -- 删除所有未跟踪文件 }) ``` ## git.apply * 应用补丁文件 #### 函数原型 ::: tip API ```lua git.apply(patch: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | patch | 必需。补丁文件路径 | | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录- `reverse` - 是否反向应用- `gitdir` - git目录 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 应用成功返回 true,失败返回 false | #### 用法说明 此接口对应`git apply`命令,用于应用diff格式的补丁文件。 ```lua import("devel.git") -- 应用补丁 git.apply("fix.patch", { repodir = "/tmp/xmake", reverse = true, -- 反向应用补丁 gitdir = ".git" -- 指定git目录 }) -- 应用diff文件 git.apply("changes.diff") ``` ## git.branch * 获取当前分支名 #### 函数原型 ::: tip API ```lua git.branch(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回当前分支名 | #### 用法说明 此接口对应`git branch --show-current`命令,返回当前所在分支的名称。 ```lua import("devel.git") -- 获取当前分支 local branch = git.branch({repodir = "/tmp/xmake"}) print("Current branch:", branch) ``` ## git.lastcommit * 获取最新提交哈希 #### 函数原型 ::: tip API ```lua git.lastcommit(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回最新提交哈希 | #### 用法说明 此接口对应`git rev-parse HEAD`命令,返回仓库的最新提交哈希值。 ```lua import("devel.git") -- 获取最新提交哈希 local commit = git.lastcommit({repodir = "/tmp/xmake"}) print("Last commit:", commit) ``` ## git.refs * 获取所有引用列表 #### 函数原型 ::: tip API ```lua git.refs(url: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 必需。远程仓库URL | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回引用列表数组 | #### 用法说明 此接口对应`git ls-remote --refs`命令,返回远程仓库的所有引用。 ```lua import("devel.git") -- 获取所有引用 local refs = git.refs("https://github.com/xmake-io/xmake.git") for _, ref in ipairs(refs) do print("Ref:", ref) end ``` ## git.tags * 获取所有标签列表 #### 函数原型 ::: tip API ```lua git.tags(url: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 必需。远程仓库URL | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回标签列表数组 | #### 用法说明 此接口对应`git ls-remote --tags`命令,返回远程仓库的所有标签。 ```lua import("devel.git") -- 获取所有标签 local tags = git.tags("https://github.com/xmake-io/xmake.git") for _, tag in ipairs(tags) do print("Tag:", tag) end ``` ## git.branches * 获取所有分支列表 #### 函数原型 ::: tip API ```lua git.branches(url: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 必需。远程仓库URL | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回分支列表数组 | #### 用法说明 此接口对应`git ls-remote --heads`命令,返回远程仓库的所有分支。 ```lua import("devel.git") -- 获取所有分支 local branches = git.branches("https://github.com/xmake-io/xmake.git") for _, branch in ipairs(branches) do print("Branch:", branch) end ``` ## git.ls\_remote * 获取远程引用信息 #### 函数原型 ::: tip API ```lua git.ls_remote(refs: , url: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | refs | 必需。引用类型,例如 "tags", "heads", "refs" | | url | 必需。远程仓库URL | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回引用列表数组 | #### 用法说明 此接口对应`git ls-remote`命令,支持获取标签、分支或所有引用。 ```lua import("devel.git") -- 获取标签 local tags = git.ls_remote("tags", "https://github.com/xmake-io/xmake.git") -- 获取分支 local heads = git.ls_remote("heads", "https://github.com/xmake-io/xmake.git") -- 获取所有引用 local refs = git.ls_remote("refs") ``` ## git.checkurl * 检查是否为git URL #### 函数原型 ::: tip API ```lua git.checkurl(url: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 必需。要检查的URL | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 是git URL返回 true,否则返回 false | #### 用法说明 检查给定的URL是否为有效的git仓库地址。 ```lua import("devel.git") -- 检查URL local is_git = git.checkurl("https://github.com/xmake-io/xmake.git") if is_git then print("This is a git URL") end ``` ## git.asgiturl * 转换URL为git格式 #### 函数原型 ::: tip API ```lua git.asgiturl(url: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 必需。要转换的URL | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回标准git URL格式 | #### 用法说明 将各种格式的URL转换为标准的git URL格式,支持自定义协议。 ```lua import("devel.git") -- 转换URL为git格式 local git_url = git.asgiturl("github:xmake-io/xmake") print("Git URL:", git_url) -- https://github.com/xmake-io/xmake.git -- 支持的自定义协议 local protocols = { "github:user/repo", -- https://github.com/user/repo.git "gitlab:user/repo", -- https://gitlab.com/user/repo.git "gitee:user/repo", -- https://gitee.com/user/repo.git "bitbucket:user/repo" -- https://bitbucket.org/user/repo.git } ``` ## submodule.update * 更新子模块 #### 函数原型 ::: tip API ```lua submodule.update(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录- `init` - 是否初始化子模块- `remote` - 是否更新远程信息- `recursive` - 是否递归更新- `force` - 是否强制更新- `checkout` - 是否检出子模块- `merge` - 是否合并模式- `rebase` - 是否变基模式- `reference` - 引用仓库- `paths` - 指定子模块路径- `longpaths` - 启用长路径支持 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 更新成功返回 true,失败返回 false | #### 用法说明 此接口对应`git submodule update`命令,用于更新仓库的子模块。 ```lua import("devel.git.submodule") -- 基本更新 submodule.update({repodir = "/tmp/xmake"}) -- 带选项的更新 submodule.update({ repodir = "/tmp/xmake", init = true, -- 初始化子模块 remote = true, -- 更新远程信息 recursive = true, -- 递归更新 force = true, -- 强制更新 checkout = true, -- 检出子模块 merge = true, -- 合并模式 rebase = true, -- 变基模式 reference = "/path/to/repo", -- 引用仓库 paths = {"submodule1", "submodule2"}, -- 指定子模块路径 longpaths = true -- 启用长路径支持 }) ``` ## submodule.clean * 清理子模块 #### 函数原型 ::: tip API ```lua submodule.clean(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录- `force` - 是否强制删除- `all` - 是否删除所有未跟踪文件 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 清理成功返回 true,失败返回 false | #### 用法说明 此接口对应`git submodule foreach git clean`命令,用于清理所有子模块的未跟踪文件。 ```lua import("devel.git.submodule") -- 基本清理 submodule.clean({repodir = "/tmp/xmake"}) -- 强制清理 submodule.clean({ repodir = "/tmp/xmake", force = true, -- 强制删除 all = true -- 删除所有未跟踪文件 }) ``` ## submodule.reset * 重置子模块 #### 函数原型 ::: tip API ```lua submodule.reset(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `repodir` - 仓库目录- `hard` - 是否硬重置- `soft` - 是否软重置- `commit` - 重置到指定提交- `longpaths` - 启用长路径支持 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 重置成功返回 true,失败返回 false | #### 用法说明 此接口对应`git submodule foreach git reset`命令,用于重置所有子模块的状态。 ```lua import("devel.git.submodule") -- 基本重置 submodule.reset({repodir = "/tmp/xmake"}) -- 硬重置 submodule.reset({ repodir = "/tmp/xmake", hard = true, -- 硬重置 commit = "HEAD" -- 重置到指定提交 }) -- 软重置 submodule.reset({ repodir = "/tmp/xmake", soft = true, -- 软重置 longpaths = true -- 启用长路径支持 }) ``` --- --- url: /guide/extras/distributed-compilation.md --- # Distributed Compilation Xmake provides a built-in distributed compilation service. Usually, it can cooperate with local compilation cache and remote compilation cache to achieve optimal compilation acceleration. It is fully cross-platform supported; we not only support GCC and Clang, but also Windows and MSVC as well. For cross-compilation, as long as the cross-toolchain supports it, we do not require the system environment of the server. Even if the server resources of Linux, macOS, and Windows are mixed, distributed compilation can be realized to its fullest potential! ## Start the service We can specify the `--distcc` parameter to enable the distributed compilation service. Of course, if this parameter is not specified, Xmake will enable all server-configured services by default. Here we assume that there are two machines as a distributed compilation server cluster, with the IP addresses `192.168.22.168` and `192.168.22.169`. On the two servers execute the following command: ```sh $ xmake service --distcc : listening 0.0.0.0:9093 .. ``` Or for a more verbose experience with more detailed log messages, run it with the `-vD` flag. ```sh $ xmake service --distcc -vD : listening 0.0.0.0:9093 .. ``` ## Start the service in Daemon mode To start the service in daemon mode, and control it, run: ```sh $ xmake service --distcc --start $ xmake service --distcc --restart $ xmake service --distcc --stop ``` ## Configure the server We first, run the `xmake service` command, it will automatically generate a default `server.conf` configuration file, stored in `~/.xmake/service/server.conf`. ```sh $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. an token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9693 .. ``` Then, we edit it, fixing the server's listening port (optional). ```sh { distcc_build = { listen = "0.0.0.0:9693", workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` ## Configure the client The client configuration file is in `~/.xmake/service/client.conf`, where we can configure the server address that the client needs to connect to. We can configure multiple server addresses and corresponding tokens in the hosts list. With distributed compilation, it is recommended to use the token authentication mode, because the password mode requires a password to be entered for each server connection, which can become very annoying very fast. ```sh { distcc_build = { hosts = { { connect = "192.168.22.168:9693", token = "590234653af52e91b9e438ed860f1a2b" }, { connect = "192.168.22.169:9693", token = "590234653af52e91b9e438ed860f1a2b" } } } } } ``` ### Timeout configuration By default, clients connect, send, and receive data with unlimited waiting without timeout, but if the network to access the server is unstable, then there is a chance that access may get stuck, and this can be solved by configuring a timeout. If a timeout exception occurs, it will automatically degrade to local compilation and will not be stuck forever. We can configure, `send_timeout`, `recv_timeout` and `connect_timeout` to take effect for all client services if set at the root. ```sh { send_timeout = 5000, recv_timeout = 5000, connect_timeout = 5000 } ``` We can also configure the timeout just for the current distributed build service, leaving the other services with the default timeout. ```sh { distcc_build = { send_timeout = 5000, recv_timeout = 5000, connect_timeout = 5000, } } ``` The server-side configuration also supports timeout configuration. ## User authorization For user authorization, please refer to [Remote Compilation/User Authorization](/guide/extras/remote-compilation#user-authorization). ## Connect to the server After configuring the authentication and server address, you can enter the following command to connect the current project to the configured server. We need to enter `--distcc` when connecting to specify that only distributed services are connected. ```sh $ cd projectdir $ xmake service --connect --distcc : connect 127.0.0.1:9693 .. : 127.0.0.1:9693 connected! ``` We can also connect to multiple services at the same time, such as distributed compilation and remote compilation cache services. ```sh $ xmake service --connect --distcc --ccache ``` If there is no parameter, the default connection is the remote compilation service. ## Distributed compilation project After connecting to the server, we can perform distributed compilation transparently (e.g. just like normal local compilation). For example: ```sh $ xmake ... [ 93%]: cache compiling.release src/demo/network/unix_echo_client.c ----> local job [ 93%]: cache compiling.release src/demo/network/ipv6.c [ 93%]: cache compiling.release src/demo/network/ping.c [ 93%]: distcc compiling.release src/demo/network/unix_echo_server.c. ----> distcc job [93%]: distcc compiling.release src/demo/network/http.c [ 93%]: distcc compiling.release src/demo/network/unixaddr.c [ 93%]: distcc compiling.release src/demo/network/ipv4.c [ 94%]: distcc compiling.release src/demo/network/ipaddr.c [94%]: distcc compiling.release src/demo/math/fixed.c [94%]: distcc compiling.release src/demo/libm/float.c [ 95%]: cache compiling.release src/demo/libm/double.c [ 95%]: cache compiling.release src/demo/other/test.cpp [ 98%]: archiving.release libtbox.a [99%]: linking.release demo [100%]: build ok! ``` Among them, the words with distcc are remote compilation tasks, and the others are local compilation tasks. By default, xmake also enables local compilation caching to cache distributed compilation results to avoid frequent requests to the server. In addition, we can also enable the remote compilation cache and share the compilation cache with others to further accelerate the compilation of multi-person collaborative development. ## Disconnect You can disconnect from a compilation server with: ```sh $ xmake service --disconnect --distcc ``` ## Specify the number of parallel compilation tasks Let's briefly introduce the number of parallel tasks currently calculated by default based on the number of host CPU cores: ```lua -- let n = number of CPUs -- let deafult_njob = ⌈(3n) / 2⌉ local default_njob = math.ceil(ncpu * 3 / 2) ``` Therefore, if distributed compilation is not enabled, the default maximum number of parallel compilation tasks is `default_njob`. If distributed compilation is enabled, the default number of parallel compilation tasks is: ```lua -- let n = default number of jobs -- let c = server count -- let d = server's default number of jobs -- let maxjobs = (n + c)d local maxjobs = default_njob + server_count * server_default_njob ``` ### Modify the number of local parallel tasks We only need to pass `-jN` to specify the number of local parallel tasks (just like in Make), but it will not affect the number of parallel tasks on the server side. ```sh $ xmake -jN ``` ### Modify the number of parallel tasks on the server If you want to modify the number of parallel tasks on the server, you need to modify the configuration file of the client. ```sh { distcc_build = { hosts = { { connect = "127.0.0.1:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 8 <------- modify here }, { connect = "192.168.01:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 4 } } } } ``` For each server host, add the `njob = N` parameter configuration to specify the number of parallel jobs that this server can provide. ## Distributed compilation of Android projects The distributed compilation service provided by Xmake is completely cross-platform and supports Windows, Linux, macOS, Android, iOS. It even supports cross compilation, so you can have a network of servers used for cross compilation that might be running macOS, Linux, or Windows. No lock in! If you want to compile an Android project, for example, you only need to add the `toolchains` toolchain configuration in the server configuration, and provide the path of the NDK. ```sh { distcc_build = { listen = "0.0.0.0:9693", toolchains = { ndk = { ndk = "~/files/android-ndk-r21e" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` Then, we can compile the Android project in a distributed way like normal local compilation, and even configure multiple Windows, macOS, Linux and other different server hosts, as the resources of the distributed compilation service, to compile it. Just download the NDK for the corresponding platform. ```sh $ xmake f -p android --ndk=~/files/xxxx $ xmake ``` ## Distributed compilation of iOS projects Compiling iOS projects is easier, because Xmake can usually automatically detect Xcode, so just switch the platform to iOS like you would normally do for local development. ```sh $ xmake f -p iphoneos $ xmake ``` ## Distributed cross compilation configuration If we want to distribute cross-compilation, we need to configure the toolchain SDK path on the server, for example: ```sh { distcc_build = { listen = "0.0.0.0:9693", toolchains = { cross = { sdkdir = "~/files/arm-linux-xxx" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` Among them, under toolchains, each item corresponds to a toolchain, here is configured as `cross = {}` cross toolchain, corresponding to `toolchain("cross")`. In the toolchain, we can configure `sdkdir`, `bindir`, `cross`, etc., corresponding to the interface configuration of `set_sdkdir`, `set_bindir` and `set_cross` in `toolchain("cross")`. If the cross toolchain is more standardized, we usually only need to configure `sdkdir`, and xmake can automatically detect it. On the client side, \compilation only needs to specify the SDK directory. ```sh $ xmake f -p cross --sdk=/xxx/arm-linux-xxx $ xmake ``` ## Clean the server cache The compilation of each project on the server side will generate some cache files, which are stored according to the project granularity. We can use the following command to clear the cache corresponding to each server for the current project. ```sh $ xmake service --clean --distcc ``` ## Some internal optimizations 1. Cache server-side compilation results to avoid repeated compilation 2. Local cache, remote cache optimization, avoid unnecessary server communication 3. Server load balancing scheduling, rational allocation of server resources 4. Small files are compiled directly locally after preprocessing, which is usually faster 5. Real-time compression and transmission of large files, based on lz4 fast compression 6. Internal state maintenance, compared to independent tools such as distcc, avoids frequent independent process loading and time-consuming, and avoids additional communication with the daemon process --- --- url: /guide/extras/environment-variables.md --- # Environment Variables We can execute the following command to get all the environment variables used by xmake and the currently set values. ```sh $ xmake show -l envs XMAKE_RAMDIR Set the ramdisk directory. XMAKE_GLOBALDIR Set the global config directory of xmake. /Users/ruki XMAKE_ROOT Allow xmake to run under root. XMAKE_COLORTERM Set the color terminal environment. XMAKE_PKG_INSTALLDIR Set the install directory of packages. XMAKE_TMPDIR Set the temporary directory. /var/folders/vn/ppcrrcm911v8b4510klg9xw80000gn/T/.xmake501/211104 XMAKE_PKG_CACHEDIR Set the cache directory of packages. XMAKE_PROGRAM_DIR Set the program scripts directory of xmake. /Users/ruki/.local/share/xmake XMAKE_PROFILE Start profiler, e.g. perf, trace. XMAKE_RCFILES Set the runtime configuration files. XMAKE_CONFIGDIR Set the local config directory of project. /Users/ruki/projects/personal/xmake-docs/.xmake/macosx/x86_64 XMAKE_LOGFILE Set the log output file path. ``` ## XMAKE\_RAMDIR * Set the ramdisk directory path The ramdisk directory is the directory location of the memory file system. Usually, the `os.tmpdir()` interface will use the temporary files used by xmake. If the user sets the ramdisk path, it will be stored here first to improve the overall compilation speed. ## XMAKE\_TMPDIR * Set the user's temporary directory By default, xmake will use `/tmp/.xmake` and `%TEMP%/.xmake`. Of course, users can modify the default path through this variable. ## XMAKE\_CONFIGDIR * Set local project configuration directory The local compilation configuration of each project will be stored in the `.xmake` path in the root directory of the current project by default, and then differentiated according to different platforms and architectures, for example: ```sh .xmake/macosx/x86_64 ``` If we don't want to store it in the root directory of the project, we can also set it to other paths ourselves, such as the build directory, and so on. ## XMAKE\_GLOBALDIR * Set the root directory of the global configuration file A `.xmake` directory, which serves as the storage directory of the global configuration of `xmake g/global` configuration, will be created under this path. Other global files such as installation packages, caches, etc., will be stored in this directory by default. The default path is: `~`. ## XMAKE\_ROOT -Allow users to run in root mode Usually, xmake is forbidden to run under root by default, which is very insecure. But if the user has to run under root, they can also set this variable to force it on. ```sh export XMAKE_ROOT=y ``` ## XMAKE\_COLORTERM * Set the color output of Terminal Currently, these values can be set: | Value | Description | | --- | --- | | nocolor | Disable color output | | color8 | 8-color output support | | color256 | 256 color output support | | truecolor | True color output support | Generally, users don't need to set them; Xmake will automatically detect the color range supported by the user terminal. If the user doesn't want to output colors, they can set nocolor to disable them globally. Or use `xmake g --theme=plain` to disable it globally. ## XMAKE\_PKG\_INSTALLDIR * Set the installation root directory of the dependent package The default global directory for xmake's remote package installation is `$XMAKE_GLOBALDIR/.xmake/packages`, but users can also set this variable to modify it individually. We can also use `xmake g --pkg_installdir=/xxx` to set it, the effect is the same. However, the environment variable takes precedence over this configuration. ## XMAKE\_PKG\_CACHEDIR * Set the cache directory of dependent packages The default path is in the `$XMAKE_GLOBALDIR/.xmake/cache` directory, which stores various cache files during the package installation process, which takes up more storage space, and the user can also set it separately. Of course, Xmake will automatically clean up all cache files of the previous month every month. ## XMAKE\_PROGRAM\_DIR * Set the script directory of Xmake All lua scripts of Xmake are installed with the installer. By default, they are in the installation directory. However, if you want to switch to the script directory you downloaded to facilitate local modification and debugging, you can set this variable. If you want to view the script directory currently used by Xmake, you can execute: ```sh $ xmake l os.programdir /Users/ruki/.local/share/xmake ``` ## XMAKE\_PROFILE * Enable performance analysis This is only available to xmake developers, and is used to analyze the time spent running xmake and track the calling process. ### Analyze the time taken to call functions ```sh $ XMAKE_PROFILE=perf:call xmake [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! 0.238, 97.93%, 1, runloop : @programdir/core/base/scheduler.lua: 805 0.180, 74.04%, 25, _resume : [C]: -1 0.015, 6.34%, 50, _co_groups_resume : @programdir/core/base/scheduler.lua: 299 0.011, 4.37%, 48, wait : @programdir/core/base/poller.lua: 111 0.004, 1.70%, 62, status : @programdir/core/base/scheduler.lua: 71 0.004, 1.53%, 38, is_dead : @programdir/core/base/scheduler.lua: 76 0.003, 1.44%, 50, next : @programdir/core/base/timer.lua: 74 0.003, 1.33%, 48, delay : @programdir/core/base/timer.lua: 60 0.002, 1.02%, 24, is_suspended : @programdir/core/base/scheduler.lua: 86 ``` ### Analysis of process time consumption It can be used to analyze the compilation time of each file and some operation bottlenecks. ```sh $ XMAKE_PROFILE=perf:process xmake -r [ 7%]: compiling.release src/header.h [ 23%]: compiling.release src/test.cpp [ 30%]: compiling.release src/test8.cpp [ 38%]: compiling.release src/test4.cpp [ 46%]: compiling.release src/test5.cpp [ 53%]: compiling.release src/test7.cpp [ 61%]: compiling.release src/test6.cpp [ 69%]: compiling.release src/test2.cpp [ 76%]: compiling.release src/main.cpp [ 84%]: compiling.release test3.cpp [ 84%]: compiling.release src/test.c [ 92%]: linking.release main [100%]: build ok, spent 2.754s 1411.000, 22.19%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_37317EEDB62F4F3088AF6A2E2A649460 -fdiagnostics-color=always -x c++-header -o build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch src/header.h 508.000, 7.99%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_1ABAE1FAD68D45008DC76A3A00697820 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/main.cpp.o src/main.cpp 473.000, 7.44%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_1C0BE5280C6F4E208F919577A48AAA40 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/test3.cpp.o test3.cpp 451.000, 7.09%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_877D3D9B6BBA4D308BFB5E4EBD751340 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test6.cpp.o src/test6.cpp 404.000, 6.35%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_C9968E2873B648208A8C3F2BA7573640 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test7.cpp.o src/test7.cpp 402.000, 6.32%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_7F6DFA37FF494D208EADF9737484EC40 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test2.cpp.o src/test2.cpp 383.000, 6.02%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_63C9E23AE7E047308F762C7C02A56B50 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test4.cpp.o src/test4.cpp 374.000, 5.88%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_C3A0EF96A7C14D00879BFAEFD26E9D20 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test8.cpp.o src/test8.cpp 368.000, 5.79%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_BADB46AF75CB4610857EF5083BD54D30 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test.cpp.o src/test.cpp 363.000, 5.71%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_0247BDB87DD14500816471184D4E8140 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test5.cpp.o src/test5.cpp 156.000, 2.45%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fPIC -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F0FF8220B33B46208D39A98937D55E50 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.c 133.000, 2.09%, 3, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang --version 107.000, 1.68%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -O3 -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_C8A96266E0034C20898C147FC52F3A40 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.c 105.000, 1.65%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fdiagnostics-color=always -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_48A2FA7BE7AB44008B60558E412A9D30 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.c 105.000, 1.65%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -fPIC -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -lz -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -lz -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F510FB15C9A647108111A7010EFED240 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp 91.000, 1.43%, 3, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ --version 74.000, 1.16%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -O3 -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_BF6B4B6DACB843008E822CEFDC711230 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test.c.o src/test.c 73.000, 1.15%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -o build/macosx/x86_64/release/main build/.objs/main/macosx/x86_64/release/src/test.cpp.o build/.objs/main/macosx/x86_64/release/src/test8.cpp.o build/.objs/main/macosx/x86_64/release/src/test4.cpp.o build/.objs/main/macosx/x86_64/release/src/test5.cpp.o build/.objs/main/macosx/x86_64/release/src/test7.cpp.o build/.objs/main/macosx/x86_64/release/src/test6.cpp.o build/.objs/main/macosx/x86_64/release/src/test2.cpp.o build/.objs/main/macosx/x86_64/release/src/main.cpp.o build/.objs/main/macosx/x86_64/release/test3.cpp.o build/.objs/main/macosx/x86_64/release/src/test.c.o -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -lz -Wl,-x -Wl,-dead_strip 70.000, 1.10%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fPIC -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_6D0B6327841A47208939EEF194F38B50 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp 68.000, 1.07%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -O3 -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_8AB279F8450D4D108E92951CC9C1C650 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp 65.000, 1.02%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fdiagnostics-color=always -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_D25F0DB04D6D430084C098F1E1F76C00 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp ``` ### Tracking the running process of xmake ```sh $ XMAKE_PROFILE=trace xmake func : @programdir/core/base/scheduler.lua: 457 is_suspended : @programdir/core/base/scheduler.lua: 86 status : @programdir/core/base/scheduler.lua: 71 thread : @programdir/core/base/scheduler.lua: 66 thread : @programdir/core/base/scheduler.lua: 66 length : @programdir/core/base/heap.lua: 120 ``` ### Analysis and operation stuck problem It can be used to get the stack when xmake is stuck. After enabling this feature, you can get the stack after interrupting by pressing Ctrl+C. ```sh $ XMAKE_PROFILE=stuck xmake l test.lua stack traceback: [C]: in function 'base/io.file_read' @programdir/core/base/io.lua:177: in method '_read' @programdir/core/sandbox/modules/io.lua:90: in function <@programdir/core/sandbox/modules/io.lua:89> (...tail calls...) /Users/ruki/share/test.lua:2: in function (...tail calls...) @programdir/plugins/lua/main.lua:123: in function <@programdir/plugins/lua/main.lua:79> (...tail calls...) [C]: in function 'xpcall' @programdir/core/base/utils.lua:280: in function 'sandbox/modules/utils.trycall' (...tail calls...) @programdir/core/base/task.lua:519: in function 'base/task.run' @programdir/core/main.lua:278: in upvalue 'cotask' @programdir/core/base/scheduler.lua:371: in function <@programdir/core/base/scheduler.lua:368> ``` ## XMAKE\_RCFILES * Set up a global configuration file We can set up some `xmakerc.lua` global configuration files, and introduce them globally when users compile the project, such as global introduction of some user-defined help scripts, tool chains and so on. ```sh $ export XMAKE_RCFILES=xmakerc.lua $ xmake ``` If this environment variable is not set, users can set the global configuration file in `/etc/xmakerc.lua`, `~/xmakerc.lua`, and `$XMAKE_GLOBALDIR/.xmake/xmakerc.lua`. The search priority is listed from highest to lowest. ## XMAKE\_LOGFILE * Set the log file path By default, Xmake will echo the output to the terminal. We can turn on the automatic log storage to the specified file by setting this path, but it will not affect the normal echo output of the terminal. ## XMAKE\_MAIN\_REPO * Set the official package master warehouse address Xmake has built-in three main warehouse addresses by default, they are exactly the same, Xmake will choose the best address to use according to the current network status. 1. https://github.com/xmake-io/xmake-repo.git 2. https://gitlab.com/tboox/xmake-repo.git 3. https://gitee.com/tboox/xmake-repo.git However, if Xmake chooses the wrong one, it may cause the warehouse download to fail. Through this environment variable, we can set and use the specified warehouse address by ourselves instead of automatically selecting it. ```sh $ export XMAKE_MAIN_REPO=https://github.com/xmake-io/xmake-repo.git ``` ## XMAKE\_BINARY\_REPO * Set the official package pre-compiled warehouse address Similar to `XMAKE_MAIN_REPO`, the only difference is that this is used to switch the address of the pre-compiled warehouse. ```sh $ export XMAKE_BINARY_REPO=https://github.com/xmake-mirror/build-artifacts.git ``` ## XMAKE\_THEME * Set theme Usually we can set the color theme through `xmake g --theme=plain`, but it is global. If we want to set the current terminal session individually, we can use this environment variable to set it. ```sh $ export XMAKE_THEME=plain ``` ## XMAKE\_STATS * Enable or disable user statistics Since Xmake is still in the early stages of development, we need to know the approximate user growth in order to provide us with the motivation to continue to update Xmake. Therefore, Xmake defaults to the first project build every day, and it will automatically git clone an empty warehouse in the background process: https://github.com/xmake-io/xmake-stats Then borrow the Traffic statistics chart provided by github itself to get the approximate number of users. For each project, we will only count once a day, and will not disclose any user privacy, because there is only one additional git clone operation. In addition, we cloned an empty warehouse, which will not consume much user traffic. Of course, not every user wants to do this, user has right to disable this behavior, we only need to set: ```sh export XMAKE_STATS=false ``` It can be completely disabled, and we will also automatically disable this behavior on ci. When will it be removed? This behavior will not exist forever. When Xmake has enough users, or there are other better statistical methods, we will consider removing the relevant statistical code. Of course, if a lot of user feedback is unwilling to accept it, we will also consider removing it. For related issues about this, see [#1795](https://github.com/xmake-io/xmake/issues/1795) and [#1803](https://github.com/xmake-io/xmake/discussions/1802). --- --- url: /guide/best-practices/faq.md --- # FAQ ## How to get command-line arguments information? Get the help info of the main command: ```sh $ xmake [-h|--help] ``` Get the help info of the configuration command: ```sh $ xmake f [-h|--help] ``` Get the help info of the given action or plugin command: ```sh $ xmake [action|plugin] [-h|--help] ``` For example: ```sh $ xmake run --help ``` ## How to suppress all output info? ```sh $ xmake [-q|--quiet] ``` ## What to do if Xmake fails? Please attempt to clean the configuration and rebuild it first. ```sh $ xmake f -c $ xmake ``` If it fails again, please add `-v` or `--verbose` options to get more verbose info. For example: ```sh $ xmake [-v|--verbose] ``` And add `-D` to get the verbose backtrace and diagnostic info, then you can submit this to [issues](https://github.com/xmake-io/xmake/issues). ```sh $ xmake -v -D ``` ## How to see verbose compiling warnings? ```sh $ xmake [-w|--warning] ``` ## Why is xmake.lua being executed multiple times? Xmake.lua is divided into description fields and script fields. In the description field, various configuration fields are parsed multiple times in stages, and it is possible to execute multiple times. Therefore, do not write complex scripts in the description field. If you want to write a variety of complex scripts, please configure them in the script domain. The script domain of `target/on_load` can also flexibly configure various target-related settings and provide more powerful Lua script module support. See: [Description of Syntax Description](/guide/project-configuration/syntax-description) for more details. ## How to debug Xmake source code? ### Downloading source code Since Xmake uses git submodules to maintain submodules, we can pull the full source code in several ways. #### Cloning with git ```sh $ git clone --recursive https://github.com/xmake-io/xmake.git ``` or ```sh $ git clone https://github.com/xmake-io/xmake.git $ git submodule update --init ``` #### Downloading source packages from Github Releases Because github's own downloads attachments do not support archiving submodules, Xmake packages an extra tarball of source code for each release and uploads it to Releases. Therefore, do not download the wrong link address * Incomplete source code: https://github.com/xmake-io/xmake/archive/refs/tags/v2.7.2.tar.gz * Full source package: https://github.com/xmake-io/xmake/releases/download/v2.7.2/xmake-v2.7.2.tar.gz ```sh $ wget https://github.com/xmake-io/xmake/releases/download/v2.7.2/xmake-v2.7.2.tar.gz $ tar -xvf xmake-v2.7.2.tar.gz -C xmake $ cd xmake ``` ::: tip NOTE The Xmake tarball does not have a top-level xmake root directory, so it is best to unpack it with `-C xmake` to specify the output directory. ::: ### Compiling source code #### Compiling on Windows If you are compiling Xmake source code on Windows, you will need to bootstrap it with an existing Xmake pre-build. Therefore we need to first install Xmake by referring to the [Installing Xmake on Windows](/guide/quick-start#windows) documentation. Then go to the Xmake source directory and compile. ```sh cd xmake cd core xmake ``` ! > We need to go into the core subdirectory of Xmake and execute the xmake command. #### Compiling on Linux/macOS/FreeBSD To compile Xmake on other unix-like environments, we just need to execute make in the source root. ```sh $ cd xmake $ ./configure $ make ``` ::: tip NOTE On macOS, you may need to run `export SDKROOT=$(xcrun --sdk macosx --show-sdk-path)` before configuration so header files can be found at build time. ::: ### Loading debugging If the compilation is complete, we can load the Xmake binary core we just compiled and run the local Lua script. #### Loading the local debugging environment on Windows Go to the `xmake/scripts` directory and double-click on the srcenv.bat script, which will automatically load the local Xmake program and scripts and open a cmd terminal. From this terminal, we can then enable debugging. We can also run: ```sh $ xmake l os.programdir ``` ...to verify that we have actually loaded the local Lua scripting environment. #### Loading a local debugging environment on other platforms On Linux/macOS/FreeBSD it's a bit easier! Just run. ```sh $ cd xmake $ source scripts/srcenv.profile ``` to get into the local source debugging environment. ### Debugging core binary Normally, to debug Xmake's Lua scripts, you just need to modify the Lua scripts in the current source directory, which takes effect in real time, and we don't need to recompile the core binary. However, if there is a problem with Xmake's C-side core program and you need to debug it or add modules to it, you will need to recompile it. You can use Xmake's internal logging functions like so to aid in debugging: ```c tb_trace_i("hello %s", "xmake"); ``` If there is a problem with the various submodules that Xmake relies on, such as tbox, and you need to debug it, you can also go directly to the submodule source code, modify it, and recompile it for execution. However, if we need to contribute a patch, we need to submit a PR to the submodule's repository and the patch will be merged and synced to the Xmake source repository by the author at a later date and time. ### Breakpoint Debugging In version 2.8.3, we added Lua breakpoint debugging support, with [VSCode-EmmyLua](https://github.com/EmmyLua/VSCode-EmmyLua) plugin, we can easily debug Xmake source code in VSCode breakpoints. First of all, we need to install the VSCode-EmmyLua plugin in VSCode's plugin market, and then run the following command to update the xmake-repo repository to keep it up-to-date. ```sh xrepo update-repo ``` ::: tip NOTE Xmake also needs to be kept up to date. ::: Then, execute the following command in your own project directory: ```sh $ xrepo env -b emmylua_debugger -- xmake build ``` The `xrepo env -b emmylua_debugger` is used to bind the EmmyLua debugger plugin environment, and the arguments after `--` are the actual xmake commands we need to debug. Usually we just debug the `xmake build` build, but if you want to debug other commands, you can tweak it yourself, for example, if you want to debug the `xmake install -o /tmp` install command, you can change it to: ```sh $ xrepo env -b emmylua_debugger -- xmake install -o /tmp ``` After executing the above command, it will not exit immediately, it will remain in a waiting debugging state, possibly without any output. At this point, instead of exiting it, let's go ahead and open VSCode and open Xmake's Lua script source directory in VSCode. That is, this directory: [Xmake Lua Scripts](https://github.com/xmake-io/xmake/tree/master/xmake), which we can download locally or directly open the lua script directory in the Xmake installation directory. Then switch to VSCode's debugging tab and click `RunDebug` -> `Emmylua New Debug` to connect to our `xmake build` command debugger and start debugging. As you can see below, the default start breakpoint will automatically break inside `debugger:_start_emmylua_debugger`, and we can click on the single-step to jump out of the current function, which will take us to the main entry. ![](/assets/img/manual/xmake-debug.png) Then set your own breakpoint and click Continue to Run to break to the code location you want to debug. We can also set breakpoints in our project's configuration scripts, which also allows us to quickly debug our own configuration scripts, not just Xmake's own source code. ![](/assets/img/manual/xmake-debug2.png) ### Remote debugging Version 2.8.3 now supports remote debugging, but this feature is mainly for the author, because the author's development computer is a mac, but sometimes he still needs to be able to debug xmake source scripts on windows. But debugging in a virtual machine is too laggy, not good experience, and the author's own computer does not have enough disk space, so I usually connect to a separate windows host to debug xmake source code remotely. Let's start the remote compilation service on the windows machine: ```sh $ xmake service ``` Then locally, open the project directory where you want to build, make a remote connection, and then run `xmake service --sync --xmakesrc=` to synchronise the local source: ```sh $ xmake service --connect $ xmake service --sync --xmakesrc=~/projects/personal/xmake/xmake/ $ xmake build $ xmake run ``` This way, we can modify the xmake script source locally, sync it to a remote windows machine, and then execute the xmake build command remotely to get the corresponding debug output and analyse the build behaviour. We can also pull the remote files back to the local machine for analysis with the `xmake service --pull=` command. Note: See [Remote Build Documentation](/guide/extras/remote-compilation) for a detailed description of remote build features. ![](/assets/img/manual/xmake-remote.png) ## How to debug repository packages? There are many different ways to debug, here I will focus on the most common debugging method used by the author, which is to pull the xmake-repo repository directly to debug. ```sh $ git clone https://github.com/xmake-io/xmake-repo.git $ xmake l scripts/test.lua -vD --shallow zlib ``` Using the `test.lua` script command above to debug packages, we can repeatedly install and test the specified package. `--shallow` tells Xmake not to repeat the full installation of all its dependencies for each test, but only to test the current package. We can also test specific platforms, architectures, build modes, vs\_runtime and dynamic libraries, static libraries etc. ```sh $ xmake l scripts/test.lua -vD --shallow -p mingw --mingw=/xxx/sdk zlib $ xmake l scripts/test.lua -vD --shallow -p iphoneos -a arm64 zlib $ xmake l scripts/test.lua -vD --shallow -k shared --vs_runtime=MD zlib $ xmake l scripts/test.lua -vD --shallow -m debug zlib ``` ### Debugging local package source code Sometimes, due to problems with the package source and build scripts, we need to modify some code in order to continue testing the installation, and it would be very tedious to go through the debugging changes in on\_install by adding\_patches/io.replace. Therefore, we can specify `-d package_sourcedir` to allow the test script to go directly to our pre-downloaded package source directory and test the build installation without our code changes being reset each time. ```sh $ xmake l scripts/test.lua -vD --shallow -d /tmp/zlib-1.2.11 zlib ``` Once the changes have been debugged, we then generate a patch file based on the changes via `git diff > fix.patch` and configure the patch package to be applied via `add_patches` to fix the package installation. ### Remote debugging package source code We can also debug the package remotely, by first enabling the remote service: ```sh $ xmake service ``` Then pass in the `--remote` parameter to compile and test the package remotely. ```sh $ xmake l scripts/test.lua -vD --shallow --remote /tmp/zlib-1.2.11 zlib ``` ## What should I do if the download package failed to get the local issuer certificate? ```sh curl: (60) SSL certificate problem: unable to get local issuer certificate More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not To learn more about this situation and To learn more about this situation and how to fix it, please visit the web page mentioned above. ``` If you encounter the above certificate validation problem when using Xmake to install dependencies, you can try updating the cURL certificate to fix it, or just disable certificate validation in the global configuration to bypass it. ```sh $ xmake g --insecure-ssl=y ``` Of course, disabling certificate validation poses some security risks, but the good news is that packages in the xmake-repo repository have a strict sha256 checksum. Even if the download is hijacked, it will eventually be detected by xmake's sha256 checksum and treated as an invalid download. --- --- url: /api/scripts/builtin-modules/format.md --- # format * Formatting a string #### Function Prototype ::: tip API ```lua format(formatstring: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | formatstring | Format string | | ... | Variable arguments for formatting | #### Usage 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` interface, just a simplified version of the interface name. ```lua local s = format("hello %s", xmake) ``` --- --- url: /zh/api/scripts/builtin-modules/format.md --- # format * 格式化字符串 #### 函数原型 ::: tip API ```lua format(formatstring: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | formatstring | 格式字符串 | | ... | 格式化的可变参数 | #### 用法说明 如果只是想格式化字符串,不进行输出,可以使用这个接口,此接口跟`string.format`接口等价,只是个接口名简化版。 ```lua local s = format("hello %s", xmake) ``` --- --- url: /api/description/global-interfaces.md --- # Global Interfaces The global interface affects the whole project description scope and all sub-project files. ## includes ### Add sub-project files and directories #### Function Prototype ::: tip API ```lua includes(paths: , ..., { rootdir = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | paths | Sub-project file or directory path, supports pattern matching like "\*\*/xmake.lua" | | ... | Variable parameters, can pass multiple paths | | rootdir | Root directory for relative path resolution, optional | #### Usage #### Add subdirectory configuration We can use this interfaces to add sub-project files (xmake.lua) or directories with xmake.lua. ``` projectdir - subdirs - xmake.lua - src ``` Add sub-project directories. ```lua includes("subdirs") target("test") set_kind("binary") add_files("src/*.c") ``` Or add sub-project files. ```lua includes("subdirs/xmake.lua") target("test") set_kind("binary") add_files("src/*.c") ``` #### Recursively add sub-configuration We can also recursively add multiple project sub-directory files through pattern matching. ```lua includes("**/xmake.lua") target("test") set_kind("binary") add_files("src/*.c") ``` #### Add helper configurations {add-helper-configurations} Above v2.8.5 can includes include some built-in helper configuration scripts, e.g.: ```lua includes("@builtin/check") ``` will bring in some of the detection helper interfaces provided by the built-in. There's also ```lua includes("@builtin/qt") ``` will introduce some built-in Qt-related helper interfaces. Where `@builtin` tells xmake to bring in configuration scripts from the built-in includes directory. That is, the configuration file in this path: [includes](https://github.com/xmake-io/xmake/tree/master/xmake/includes) We can bring in the whole thing by directory as above, or we can bring in individual configuration files, e.g.: ```lua includes("@builtin/check/check_cfuncs.lua") ``` only introduces the helper scripts related to check\_cfuncs in the check directory. With `@builtin` we can distinguish between the files in the current user's project directory and the built-in files in the xmake installation directory. #### Scope Description {#scope-description} Configurations introduced by `includes` inherit and take effect in a tree-like hierarchy. That is, global configurations in the current `xmake.lua` file will apply to all `includes` sub-xmake.lua files. For example: ``` projectdir - xmake.lua - foo/xmake.lua - bar/xmake.lua ``` In the above structure, all `includes` configurations in `projectdir/xmake.lua` are accessible in `foo/xmake.lua` and `bar/xmake.lua`, but not vice versa. ```lua includes("foo") includes("bar") target("test") add_files("src/*.c") ``` For example, if the imported `foo/xmake.lua` contains a global `add_defines` configuration, it will not take effect on the test target because foo/xmake.lua is a child configuration and cannot affect the parent configuration. ::: Tip NOTE This scope isolation can avoid many hidden configuration conflicts and scope pollution. In highly nested project configurations, implicit global imports can cause many problems. ::: #### Modular and Reusable Configurations {#modular-reusable} So, if I want to modularize and reuse configurations, how should I do it? Simply encapsulate the configurations you want to reuse within a function, for example: ```lua [foo/xmake.lua] function add_foo_configs() add_defines("FOO") -- ... end ``` ```lua [bar/xmake.lua] function add_bar_configs() add_defines("BAR") -- ... end ``` ```lua [xmake.lua] includes("foo") includes("bar") target("test1") add_files("src/*.c") add_foo_configs() target("test2") add_files("src/*.c") add_bar_configs() ``` This approach not only avoids configuration conflicts caused by implicit global imports but also supports per-target configuration, enabling configuration module reuse and greater flexibility. If you want this to take effect globally, just move it to the global scope. ```lua [xmake.lua] includes("foo") includes("bar") add_foo_configs() add_bar_configs() target("test1") add_files("src/*.c") target("test2") add_files("src/*.c") ``` ::: Tip NOTE Also, target scope configurations can be added repeatedly. In many cases, you don't need to encapsulate functions. Simply organize your configurations with includes, then update the target configurations repeatedly in different xmake.lua files. ::: ## set\_project ### Set project name #### Function Prototype ::: tip API ```lua set_project(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Project name string | #### Usage 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 #### Function Prototype ::: tip API ```lua set_version(version: , { build = , soname = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Project version string, e.g. "1.5.1" | | build | Build version string, supports time format like "%Y%m%d%H%M" | | soname | Soname version for dynamic library compatibility control, can be string or boolean | #### Usage Set the whole project version, we can set it at the beginning of `xmake.lua`. ```lua set_version("1.5.1") ``` We can set build version in v2.1.7 version: ```lua set_version("1.5.1", {build = "%Y%m%d%H%M"}) ``` We can also add version to the config header files, @see [add\_configfiles](/api/description/project-target#add-configfiles) :::tip NOTE We can set the version globally, but now we can also set it individually in the target field. ::: Version 2.8.2 adds soname versioning support for version compatibility control of so/dylib dynamic libraries. You can configure the soname version suffix, and xmake will automatically generate a symbolic link to execute the specified version of the library when compiling and installing it. For example, if we configure: ```lua set_version("1.0.1", {soname = true}) ``` xmake will automatically resolve the major version of the version number as the soname version, generating the following structure: ``` └── lib ├── libfoo.1.0.1.dylib ├── libfoo.1.0.1.dylib -> libfoo.1.0.1.dylib └── libfoo.dylib -> libfoo.1.dylib ``` Of course, we can also specify soname to a specific version naming: ```lua set_version("1.0.1", {soname = "1.0"}) -> libfoo.so.1.0, libfoo.1.0.dylib set_version("1.0.1", {soname = "1"}) -> libfoo.so.1, libfoo.1.dylib set_version("1.0.1", {soname = "A"}) -> libfoo.so.A, libfoo.A.dylib set_version("1.0.1", {soname = ""}) -> libfoo.so, libfoo.dylib ``` And if soname is not set, then soname version control is not enabled by default: ```lua set_version("1.0.1") -> libfoo.so, libfoo.dylib ``` ## set\_xmakever ### Set minimal xmake version #### Function Prototype ::: tip API ```lua set_xmakever(version: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Minimum required xmake version string, e.g. "2.1.0" | #### Usage 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\_moduledirs ### Add module directories #### Function Prototype ::: tip API ```lua add_moduledirs(dirs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dirs | Module directory path string or array | | ... | Variable parameters, can pass multiple directory paths | #### Usage 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`](/api/scripts/builtin-modules/import). ## add\_plugindirs ### Add plugin directories #### Function Prototype ::: tip API ```lua add_plugindirs(dirs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dirs | Plugin directory path string or array | | ... | Variable parameters, can pass multiple directory paths | #### Usage 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. ## get\_config ### Get the configuration value #### Function Prototype ::: tip API ```lua get_config(name: ): ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration option name string | #### Usage 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 ``` ::: tip NOTE This interface can get not only the custom configuration option values defined through [option](/api/description/configuration-option#option), but also the built-in global and local configurations. ::: ## set\_config ### Set the default configuration value #### Function Prototype ::: tip API ```lua set_config(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration option name string | | value | Configuration value, can be string, boolean, or number | #### Usage 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("builddir", "other/builddir") 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 the required dependency packages #### Function Prototype ::: tip API ```lua add_requires(packages: , ..., { optional = , system = , verify = , debug = , private = , configs =
, alias = , ... = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | packages | Package name string or array, supports semantic version like "tbox 1.6.\*" | | ... | Variable parameters, can pass multiple package names | | optional | Set as optional package, boolean value | | system | Disable system package detection, boolean value | | verify | Disable package verification, boolean value | | debug | Use debug version of package, boolean value | | private | Use as private package, boolean value | | configs | Package-specific configuration table | | alias | Package alias name | | ... | Other package-specific configuration parameters | #### Usage The dependency package management of xmake fully supports semantic version selection, for example: "~1.6.1". For a detailed description of the semantic version, please see: #### Semantic version ```lua add_requires("tbox 1.6.*", "pcre 8.x", "libpng ^1.18") add_requires("libpng ~1.16", "zlib 1.1.2 || >=1.2.11 <1.3.0") ``` At present, the semantic version parser used by xmake is the [sv](https://github.com/uael/sv) library contributed by [uael](https://github.com/uael), which also contains the version description writing method For detailed instructions, please refer to the following: [Version Description](https://github.com/uael/sv#versions) #### Install latest version Of course, if we have no special requirements for the version of the current dependency package, we can write it directly like this: ```lua add_requires("tbox", "libpng", "zlib") ``` By default, if the version number is not set, xmake will select the latest version of the package, which is equivalent to `add_requires("zlib latest")` #### Branch selection This will use the latest known version of the package, or a package compiled from the source code of 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 dependent package is not supported by the current platform, or the compilation and installation fails, then xmake will compile an error. This is reasonable for some projects that must rely on certain packages to work. But if some packages are optional dependencies and can be compiled and used normally even if not, they can be set as optional packages: #### Git commit selection With version 2.6.5, we can select a version by specifying git commit directly for packages maintained by git. ```lua add_requires("tbox e807230557aac69e4d583c75626e3a7ebdb922f8") ``` #### Optional package ```lua add_requires("zlib", {optional = true}) ``` #### Disable system package With the default setting, xmake will first check whether the system library exists (if the version requirement is not set). If the user does not want to use the system library and the library provided by the third-party package management at all, then you can set: ```lua add_requires("zlib", {system = false}) ``` #### Disable package verification The default package installation will automatically check the integrity of the downloaded package to avoid tampering, but if you install some unknown new version of the package, it will not work. Users can install them temporarily via `{verify = false}` to forcibly disable the package integrity check (but this is generally not recommended). ```lua add_requires("zlib", {verify = false}) ``` #### Use the debug package If we want to debug the dependent packages at the same time, we can set to use the debug version of the package (of course, the premise is that this package supports debug compilation): ```lua add_requires("zlib", {debug = true}) ``` If the current package does not support debug compilation, you can submit a modification to the compilation rules in the warehouse to support debugging, 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) ``` #### Use as a private package If this package is only used for package definition, and we don’t want to export links/linkdirs information by default, it can be provided as a private package. This is usually useful when making packages. ```lua package("test") add_deps("zlib", {private = true}) on_install(function (package) local zlib = package:dep("zlib"):fetch() - TODO end) ``` If you define a test package and privately rely on a zlib package, wait for the zlib installation to complete, get the package file information inside for further processing and installation, but the zlib package itself will not export links/linkdirs. Although `add_requires` also supports this option, it does not export links/linkdirs, so it is usually not used in this way. It is only useful for making packages. #### Use dynamic libraries The default package installs a static library. If you want to enable a dynamic library, you can configure it as follows: ```lua add_requires("zlib", {configs = {shared = true}}) ``` :::tip NOTE Of course, the premise is that in the definition of this package, there is a judgment and processing of `package:config("shared")`. In the official xmake-repo repository, it is usually strictly differentiated and supported. ::: #### Disable pic support The linux packages installed by default are compiled with pic enabled, which is very useful for relying on static libraries in dynamic libraries, but if you want to disable pic, it is also possible. ```lua add_requires("zlib", {configs = {pic = false}}) ``` #### Set vs runtime The windows package installed by default is compiled with msvc/MT, if you want to switch to MD, you can configure it as follows: ```lua add_requires("zlib", {configs = {vs_runtime = "MD"}}) ``` In addition, it supports four options: MT, MTd, MD, and MDd. If there are many dependent packages, it is very troublesome to switch each configuration again. We can also switch through the `set_runtimes` global setting to take effect for all dependent packages. ```lua set_runtimes("MD") add_requires("zlib", "pcre2", "mbedtls") ``` #### Specific configuration package Some packages have various compilation options during compilation, and we can also pass them in: ```lua add_requires("boost", {configs = {context = true, coroutine = true}}) ``` For example, the boost package installed above has enabled some of its internal sub-module features (packages with coroutine module support). Of course, which configurations are specifically supported are different for each package. You can use the `xmake require --info boost` command to view the list of the configs section inside. Because, in each package definition, there will be its own configuration options, and you can use `package:config("coroutine")` to determine whether to enable them during installation. #### Install third-party manager package Currently, the following packages in the third-party package manager are supported. * Conan (conan::openssl/1.1.1g) * Conda (conda::libpng 1.3.67) * Vcpkg (vcpkg::ffmpeg) * Homebrew/Linuxbrew (brew::pcre2/libpcre2-8) * Pacman on archlinux/msys2 (pacman::libcurl) * Apt on ubuntu/debian (apt::zlib1g-dev) * Clib (clib::clibs/bytes@0.0.4) * Dub (dub::log 0.4.3) * Portage on Gentoo/Linux (portage::libhandy) For example, add conan's dependency package: ```lua add_requires("conan::zlib/1.2.11", {alias = "zlib", debug = true}) add_requires("conan::openssl/1.1.1g", {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: ```sh 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 (debug) -> conan::openssl/1.1.1g please input: y (y/n) => installing conan::zlib/1.2.11 .. ok => installing conan::openssl/1.1.1g .. ok [0%]: cache compiling.release src/main.c [100%]: linking.release test ``` For a complete introduction to this and the installation and use of all third-party packages, you can refer to the document: [Third-party dependency package installation](/guide/package-management/using-third-party-packages). #### Another simplified configuration syntax The common configuration syntax we usually use: ```lua add_requires("boost >=1.78.0", {configs = {iostreams = true, system = true, thread = true}}) ``` For most boolean configurations, we can simplify the configuration by writing as follows. ```lua add_requires("boost[iostreams,system,thread] >=1.78.0") ``` This will save a lot of trouble for installations with complex configurations under the `xrepo install` independent cli command. Users can choose to use it according to their own preferences. ```sh xrepo install boost[iostreams,system,thread] ``` In addition, in addition to boolean configurations, string and array configuration values ​​are also supported. Boolean values ​​can also be set `=n/y` to disable and enable. ```lua add_requires("boost[iostreams,system,thread,key=value] >=1.78.0") add_requires("boost[iostreams=y,thread=n] >=1.78.0") add_requires("ffmpeg[shared,debug,codecs=[foo,bar,zoo]]") ``` ## add\_requireconfs ### Set the configuration of the specified dependent package #### Function Prototype ::: tip API ```lua add_requireconfs(packages: , ..., { configs =
, override = , version = , debug = , ... = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | packages | Package name string or array, supports pattern matching like "\*" or "libpng.zlib" | | ... | Variable parameters, can pass multiple package names | | configs | Package configuration table | | override | Override existing configuration, boolean value | | version | Package version string | | debug | Use debug version, boolean value | | ... | Other package-specific configuration parameters | #### Usage This is a newly added interface after v2.5.1. We can use it to expand and rewrite the configuration of the package defined by `add_requires()` and its dependent packages. It has the following uses. #### Expand the configuration of the specified package This is the basic usage. For example, we have declared a package through `add_requires("zlib")`, and want to expand the configuration of this zlib later and change it to dynamic library compilation. You can configure it in the following way. ```lua add_requires("zlib") add_requireconfs("zlib", {configs = {shared = true}}) ``` It is equivalent to ```lua add_requires("zlib", {configs = {shared = true}}) ``` #### Set general default configuration The above usage, we still don't see any practical use, we can look at the following example first: ```lua add_requireconfs("*", {configs = {shared = true}}) add_requires("zlib") add_requires("pcre") add_requires("libpng") add_requires("libwebp") add_requires("libcurl", {configs = {shared = false}}) ``` For the above configuration, we use pattern matching through `add_requireconfs("*", {configs = {shared = true}})` to set all dependent packages to compile and install dynamic libraries by default. However, we used `add_requires("libcurl", {configs = {shared = false}})` to configure libcurl to compile and install static libraries. The final configuration result is: zlib/pcre/libpng/libwebp is a shared library, and libcurl is a static library. Through pattern matching, we can put some common configurations of each package into the unified `add_requireconfs` to pre-configure, which greatly simplifies the definition of each `add_requires`. :::tip NOTE By default, for the same configuration, xmake will give priority to the configuration in add\_requires instead of add\_requireconfs. ::: If the version is set in `add_requires("zlib 1.2.11")`, the configuration of add\_requires will be used first, and the version configuration in add\_requireconfs will be completely ignored. Of course, we can also completely override the version specified in `add_requires` through override . ```lua add_requires("zlib 1.2.11") add_requireconfs("zlib", {override = true, version = "1.2.10"}) ``` #### Rewrite package dependency configuration In fact, the biggest use of `add_requireconfs` is to allow users to rewrite the configuration of specific dependent packages of the installation package. What does it mean? For example, our project integrates the package libpng and uses a dynamic library version, but the zlib library that libpng depends on is actually a static library version. ```lua add_requires("libpng", {configs = {shared = true}}) ``` So if we want to change the zlib package that libpng depends on to be compiled as a dynamic library, how should we configure it? This requires `add_requireconfs`. ```lua add_requires("libpng", {configs = {shared = true}}) add_requireconfs("libpng.zlib", {configs = {shared = true}}) ``` Through the writing method of `libpng.zlib` dependency path, specify an internal dependency and rewrite the internal dependency configuration. If the dependency path is deep, such as the dependency chain of `foo -> bar -> xyz`, we can write: `foo.bar.xyz` We can also rewrite the internal zlib library version that libpng depends on: ```lua add_requires("libpng") add_requireconfs("libpng.zlib", {override = true, version = "1.2.10"}) ``` #### Pattern matching for cascading dependencies If a package has a lot of dependencies, and the dependency level is also very deep, what to do, for example, the package libwebp, its dependencies are: ``` libwebp - libpng - zlib - cmake - libjpeg - libtiff - zlib - giflib - cmake ``` If I want to rewrite all the dependent libraries in libwebp to add specific configuration, then the configuration one by one will be very cumbersome. At this time, the recursive dependency pattern matching of `add_requireconfs()` is needed to support. ```lua add_requires("libwebp") add_requireconfs("libwebp.**|cmake", {configs = {cxflags = "-DTEST"}}) ``` In the above configuration, we added `-DTEST` to compile all the library dependencies in libwebp, but the cmake dependency is a build tool dependency, and we can exclude it by way of `|xxx`. The pattern matching here is very similar to `add_files()`. We are giving a few examples. For example, this time we only rewrite the single-level dependency configuration under libwebp to enable the debugging library: ```lua add_requires("libwebp") add_requireconfs("libwebp.*|cmake", {debug = true}) ``` ## add\_repositories ### Add 3rd package repositories #### Function Prototype ::: tip API ```lua add_repositories(repos: , ..., { rootdir = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | repos | Repository name and URL string or array, format: "name url" | | ... | Variable parameters, can pass multiple repositories | | rootdir | Root directory for relative path resolution, optional | #### Usage 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 repository 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. Note: myrepo is the relative path of the directory where the xmake command is executed. It will not be automatically converted according to the directory where the configuration file is located. If you want to set the path relative to the current xmake.lua file, you can specify it through the rootdir parameter. ```lua add_repositories("my-repo myrepo", {rootdir = os.scriptdir()}) ``` However, this parameter setting is only supported by v2.5.7 and above. ## set\_defaultplat ### Set the default compilation platform #### Function Prototype ::: tip API ```lua set_defaultplat(platform: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | platform | Default compilation platform name, e.g. "iphoneos", "windows" | #### Usage Only supported by v2.5.6 and above, it is used to set the default compilation platform of the project. If it is not set, the default platform follows the current system platform, which is os.host(). For example, the default compilation platform on macOS is macosx, if the current project is an ios project, you can set the default compilation platform to iphoneos. ```lua set_defaultplat("iphoneos") ``` It is equivalent to `xmake f -p iphoneos`. ## set\_defaultarchs ### Set the default compilation architecture #### Function Prototype ::: tip API ```lua set_defaultarchs(archs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | archs | Default compilation architecture string or array, supports platform-specific format like "iphoneos|arm64" | | ... | Variable parameters, can pass multiple architecture specifications | #### Usage Only supported by v2.5.6 and above, it is used to set the default compilation architecture of the project. If it is not set, the default platform follows the current system architecture, which is os.arch(). ```lua set_defaultplat("iphoneos") set_defaultarchs("arm64") ``` It is equivalent to `xmake f -p iphoneos -a arm64`. We can also set the default architecture under multiple platforms. ```lua set_defaultarchs("iphoneos|arm64", "windows|x64") ``` The arm64 architecture is compiled by default on iphoneos, and the x64 architecture is compiled by default on windows. ## set\_defaultmode ### Set the default compilation mode #### Function Prototype ::: tip API ```lua set_defaultmode(mode: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | mode | Default compilation mode name, e.g. "release", "debug", "releasedbg" | #### Usage Only supported by v2.5.6 and above, it is used to set the default compilation mode of the project. If it is not set, the default is to compile in release mode. ```lua set_defaultmode("releasedbg") ``` It is equivalent to `xmake f -m releasedbg`. ## set\_allowedplats ### Set the list of platforms allowed to compile #### Function Prototype ::: tip API ```lua set_allowedplats(platforms: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | platforms | Allowed compilation platform name string or array | | ... | Variable parameters, can pass multiple platform names | #### Usage It is only supported by v2.5.6 and above. It is used to set the list of compilation platforms supported by the project. If the user specifies other platforms, an error will be prompted. This is usually used to restrict the user from specifying the wrong invalid platform. If it is not set, then there are no platform restrictions. ```lua set_allowedplats("windows", "mingw") ``` Set the current project to only support windows and mingw platforms. ## set\_allowedarchs ### Set the platform architecture that allows compilation #### Function Prototype ::: tip API ```lua set_allowedarchs(archs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | archs | Allowed compilation architecture string or array, supports platform-specific format like "windows|x64" | | ... | Variable parameters, can pass multiple architecture specifications | #### Usage Only supported by v2.5.6 and above. It is used to set the list of compiled architectures supported by the project. If the user specifies other architectures, an error will be prompted. This is usually used to restrict users from specifying incorrect invalid architectures. If it is not set, then there are no architectural restrictions. ```lua set_allowedarchs("x64", "x86") ``` The current project only supports x64/x86 platforms. We can also specify the list of architectures allowed under multiple platforms at the same time. ```lua set_allowedarchs("windows|x64", "iphoneos|arm64") ``` Set the current project to only support x64 architecture on windows, and only support arm64 architecture on iphoneos. ## set\_allowedmodes ### Set the list of allowed compilation modes #### Function Prototype ::: tip API ```lua set_allowedmodes(modes: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | modes | Allowed compilation mode name string or array | | ... | Variable parameters, can pass multiple mode names | #### Usage It is only supported by v2.5.6 and above. It is used to set the list of compilation modes supported by the project. If the user specifies other modes, an error will be prompted. This is usually used to restrict the user from specifying incorrect invalid modes. If it is not set, then there is no mode restriction. ```lua set_allowedmodes("release", "releasedbg") ``` Set the current project to only support the two compilation modes release/releasedbg. ## namespace ### Enter namespace #### Function Prototype ::: tip API ```lua namespace(name: , script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Namespace name string | | script | Namespace script function | #### Usage Enter the namespace, which is supported by xmake 2.9.8. It can be used to isolate various domain name conflicts such as duplicate target and option in sub-projects. ### Isolate target For target access within a namespace, you can directly access it in the existing way without adding any namespace. For cross-namespace access, you need to specify `namespace::` to specify it. ```lua add_rules("mode.debug", "mode.release") namespace("ns1", function () target("foo") set_kind("static") add_files("src/foo.cpp") namespace("ns2", function() target("bar") set_kind("static") add_files("src/bar.cpp") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") end) ``` When we specify to build a specific target, we can also locate it by namespace. ```sh $ xmake build -r ns1::test [ 33%]: cache compiling.release ns1::ns2::src/bar.cpp [ 41%]: cache compiling.release ns1::src/foo.cpp [ 50%]: cache compiling.release ns1::src/main.cpp [ 58%]: archiving.release ns1::ns2::libbar.a [ 75%]: archiving.release ns1::libfoo.a [ 91%]: linking.release ns1::test [100%]: build ok, spent 1.325s ``` In addition, namespaces can also isolate the configuration of the root domain. Each namespace has an independent sub-root domain and can set global configuration separately. ```lua add_rules("mode.debug", "mode.release") add_defines("ROOT") namespace("ns1", function () add_defines("NS1_ROOT") target("foo") set_kind("static") add_files("src/foo.cpp") add_defines("FOO") namespace("ns2", function () add_defines("NS2_ROOT") target("bar") set_kind("static") add_files("src/bar.cpp") add_defines("BAR") end) end) target("test") set_kind("binary") add_deps("ns1::foo", "ns1::ns2::bar") add_files("src/main.cpp") add_defines("TEST") ``` We can also isolate subprojects introduced by includes. ```lua add_rules("mode.debug", "mode.release") add_defines("ROOT") namespace("ns1", function () add_defines("NS1_ROOT") target("foo") set_kind("static") add_files("src/foo.cpp") add_defines("FOO") includes("src") end) target("test") set_kind("binary") add_deps("ns1::foo", "ns1::ns2::bar") add_files("src/main.cpp") add_defines("TEST") ``` ### Isolate option ```sh $ xmake f --opt0=y $ xmake f --ns1::opt1=y $ xmake f --ns1::ns2::opt2=y ``` ```lua add_rules("mode.debug", "mode.release") option("opt0", {default = true, defines = "OPT0", description = "option0"}) namespace("ns1", function () option("opt1", {default = true, defines = "NS1_OPT1", description = "option1"}) target("foo") set_kind("static") add_files("src/foo.cpp") add_options("opt1") namespace("ns2", function() option("opt2", {default = true, defines = "NS2_OPT2", description = "option2"}) target("bar") set_kind("static") add_files("src/bar.cpp") add_options("opt2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") add_options("opt0", "opt1", "ns2::opt2") end) ``` ### Isolate rule ```lua add_rules("mode.debug", "mode.release") rule("rule0") on_load(function (target) target:add("defines", "RULE0") end) namespace("ns1", function () rule("rule1") on_load(function (target) target:add("defines", "NS1_RULE1") end) target("foo") set_kind("static") add_files("src/foo.cpp") add_rules("rule1") namespace("ns2", function() rule("rule2") on_load(function (target) target:add("defines", "NS2_RULE2") end) target("bar") set_kind("static") add_files("src/bar.cpp") add_rules("rule2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") add_rules("rule0", "rule1", "ns2::rule2") end) ``` ### Isolate task ```sh xmake task0 xmake ns1::task1 xmake ns1::ns2::task2 ``` ```lua task("task0") set_menu {options = {}} on_run(function () print("task0") end) namespace("ns1", function () task("task1") set_menu {options = {}} on_run(function () print("NS1_TASK1") end) namespace("ns2", function() task("task2") set_menu {options = {}} on_run(function () print("NS2_TASK2") end) end) end) ``` ### Isolate toolchain ```lua toolchain("toolchain0") on_load(function (toolchain) toolchain:add("defines", "TOOLCHAIN0") end) namespace("ns1", function () toolchain("toolchain1") on_load(function (toolchain) toolchain:add("defines", "NS1_TOOLCHAIN1") end) target("foo") set_kind("static") add_files("src/foo.cpp") set_toolchains("toolchain1") namespace("ns2", function() toolchain("toolchain2") on_load(function (toolchain) toolchain:add("defines", "NS2_TOOLCHAIN2") end) target("bar") set_kind("static") add_files("src/bar.cpp") set_toolchains("toolchain2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") set_toolchains("toolchain0", "toolchain1", "ns2::toolchain2") end) ``` ### Isolate package ```lua add_requires("package0", {system = false}) package("package0") on_load(function (package) package:add("defines", "PACKAGE0") end) on_install(function (package) end) namespace("ns1", function () add_requires("package1", {system = false}) package("package1") on_load(function (package) package:add("defines", "NS1_PACKAGE1") end) on_install(function (package) end) target("foo") set_kind("static") add_files("src/foo.cpp") add_packages("package1") namespace("ns2", function() add_requires("package2", {system = false}) package("package2") on_load(function (package) package:add("defines", "NS2_PACKAGE2") end) on_install(function (package) end) target("bar") set_kind("static") add_files("src/bar.cpp") add_packages("package2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") add_packages("package0", "package1", "ns2::package2") end) ``` ## namespace\_end ### End namespace #### Function Prototype ::: tip API ```lua namespace_end() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | (none) | No parameters required | #### Usage End the current namespace. ```lua namespace("test") target("hello") add_files("src/*.c") namespace_end() ``` In addition to using namespace\_end, we can also use the following syntax to end the namespace, which is more friendly to LSP. The specific method to use depends on the user's needs and preferences. ```lua namespace("test", function () target("hello") add_files("src/*.c") end) ``` --- --- url: /api/scripts/extension-modules/core/base/graph.md --- # graph The graph module provides graph data structure with support for both directed and undirected graphs. It includes functionality for topological sorting, cycle detection, and graph manipulation. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.graph")` ::: ## graph.new * Create a new graph #### Function Prototype ::: tip API ```lua graph.new(directed: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | directed | Whether the graph is directed (true) or undirected (false) | #### Usage Creates a new graph object. The `directed` parameter specifies whether the graph is directed (true) or undirected (false). ```lua -- Create a directed graph (DAG) local dag = graph.new(true) -- Create an undirected graph local ug = graph.new(false) ``` ## graph:clear * Clear the graph #### Function Prototype ::: tip API ```lua graph:clear() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes all vertices and edges from the graph, resetting it to an empty state. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) print(#g:vertices()) -- Output: 3 g:clear() print(#g:vertices()) -- Output: 0 print(g:empty()) -- Output: true ``` ## graph:empty * Check if the graph is empty #### Function Prototype ::: tip API ```lua graph:empty() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns true if the graph contains no vertices. ```lua local g = graph.new(true) print(g:empty()) -- Output: true g:add_vertex(1) print(g:empty()) -- Output: false ``` ## graph:is\_directed * Check if the graph is directed #### Function Prototype ::: tip API ```lua graph:is_directed() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns true if the graph is directed, false if it's undirected. ```lua local dag = graph.new(true) print(dag:is_directed()) -- Output: true local ug = graph.new(false) print(ug:is_directed()) -- Output: false ``` ## graph:vertices * Get all vertices #### Function Prototype ::: tip API ```lua graph:vertices() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an array containing all vertices in the graph. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:add_vertex(4) local vertices = g:vertices() for _, v in ipairs(vertices) do print(v) -- Output: 1, 2, 3, 4 end ``` ## graph:vertex * Get vertex at the given index #### Function Prototype ::: tip API ```lua graph:vertex(idx: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | idx | Vertex index (1-based) | #### Usage Returns the vertex at the specified index (1-based). ```lua local g = graph.new(true) g:add_edge("a", "b") g:add_edge("b", "c") print(g:vertex(1)) -- Output: a print(g:vertex(2)) -- Output: b print(g:vertex(3)) -- Output: c ``` ## graph:has\_vertex * Check if the graph has the given vertex #### Function Prototype ::: tip API ```lua graph:has_vertex(v: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | v | Vertex value to check | #### Usage Returns true if the vertex exists in the graph. ```lua local g = graph.new(true) g:add_vertex(1) g:add_vertex(2) print(g:has_vertex(1)) -- Output: true print(g:has_vertex(3)) -- Output: false ``` ## graph:add\_vertex * Add an isolated vertex #### Function Prototype ::: tip API ```lua graph:add_vertex(v: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | v | Vertex value to add | #### Usage Adds a vertex to the graph without any edges. If the vertex already exists, this operation has no effect. ```lua local g = graph.new(true) g:add_vertex(1) g:add_vertex(2) g:add_vertex(3) print(#g:vertices()) -- Output: 3 print(#g:edges()) -- Output: 0 ``` ## graph:remove\_vertex * Remove the given vertex #### Function Prototype ::: tip API ```lua graph:remove_vertex(v: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | v | Vertex value to remove | #### Usage Removes a vertex and all its associated edges from the graph. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:add_edge(3, 4) g:remove_vertex(2) print(g:has_vertex(2)) -- Output: false -- Edges 1->2 and 2->3 are also removed ``` ## graph:edges * Get all edges #### Function Prototype ::: tip API ```lua graph:edges() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an array containing all edges in the graph. Each edge has `from()` and `to()` methods. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) for _, e in ipairs(g:edges()) do print(e:from(), "->", e:to()) -- Output: 1 -> 2 -- 2 -> 3 end ``` ## graph:adjacent\_edges * Get adjacent edges of the given vertex #### Function Prototype ::: tip API ```lua graph:adjacent_edges(v: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | v | Vertex value | #### Usage Returns an array of edges that are adjacent to the specified vertex. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(1, 3) g:add_edge(2, 3) local edges = g:adjacent_edges(1) for _, e in ipairs(edges) do print(e:from(), "->", e:to()) -- Output: 1 -> 2 -- 1 -> 3 end ``` ## graph:add\_edge * Add an edge #### Function Prototype ::: tip API ```lua graph:add_edge(from: , to: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | from | Source vertex | | to | Target vertex | #### Usage Adds a directed edge from `from` to `to`. For undirected graphs, this creates a bidirectional connection. Automatically creates vertices if they don't exist. ```lua -- Directed graph local dag = graph.new(true) dag:add_edge("a", "b") dag:add_edge("b", "c") -- Undirected graph local ug = graph.new(false) ug:add_edge(1, 2) -- For undirected graphs, both 1->2 and 2->1 are connected ``` ## graph:has\_edge * Check if the graph has the given edge #### Function Prototype ::: tip API ```lua graph:has_edge(from: , to: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | from | Source vertex | | to | Target vertex | #### Usage Returns true if an edge exists from `from` to `to`. ```lua local g = graph.new(true) g:add_edge(1, 2) print(g:has_edge(1, 2)) -- Output: true print(g:has_edge(2, 1)) -- Output: false (directed graph) ``` ## graph:topo\_sort * Perform topological sort #### Function Prototype ::: tip API ```lua graph:topo_sort() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Performs a topological sort on a directed graph using Kahn's algorithm. Returns an array of vertices in topological order and a boolean indicating if a cycle was detected. Only works on directed graphs. ```lua local dag = graph.new(true) dag:add_edge(0, 5) dag:add_edge(0, 2) dag:add_edge(0, 1) dag:add_edge(3, 6) dag:add_edge(3, 5) dag:add_edge(3, 4) dag:add_edge(5, 4) dag:add_edge(6, 4) dag:add_edge(6, 0) dag:add_edge(3, 2) dag:add_edge(1, 4) local order, has_cycle = dag:topo_sort() if not has_cycle then for _, v in ipairs(order) do print(v) -- Output: vertices in topological order end else print("Graph has cycle!") end ``` ::: tip TIP Topological sort is only applicable to directed acyclic graphs (DAGs). If a cycle is detected, the `has_cycle` flag will be true. ::: ## graph:partial\_topo\_sort\_reset * Reset partial topological sort state #### Function Prototype ::: tip API ```lua graph:partial_topo_sort_reset() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Resets the internal state for partial topological sorting. This must be called before starting a new partial topological sort. ```lua local dag = graph.new(true) dag:add_edge(1, 2) dag:add_edge(2, 3) dag:partial_topo_sort_reset() -- Now ready for partial topological sorting ``` ## graph:partial\_topo\_sort\_next * Get next node in topological order #### Function Prototype ::: tip API ```lua graph:partial_topo_sort_next() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the next node with zero in-degree in the topological sort. Returns nil when complete or if a cycle is detected. The `has_cycle` flag indicates if a cycle was detected. ```lua local dag = graph.new(true) dag:add_edge("a", "b") dag:add_edge("b", "c") dag:partial_topo_sort_reset() local order_vertices = {} while true do local node, has_cycle = dag:partial_topo_sort_next() if node then table.insert(order_vertices, node) dag:partial_topo_sort_remove(node) else if has_cycle then print("Cycle detected!") end break end end -- order_vertices = {"a", "b", "c"} ``` ::: tip TIP Partial topological sort allows you to process nodes incrementally and supports dynamic graph modifications during the sort. ::: ## graph:partial\_topo\_sort\_remove * Remove node and update in-degrees #### Function Prototype ::: tip API ```lua graph:partial_topo_sort_remove(node: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | node | Node to remove from the sort | #### Usage Removes the specified node from the partial topological sort and updates the in-degrees of its dependent nodes. This should be called after processing each node from `partial_topo_sort_next()`. ```lua local dag = graph.new(true) dag:add_edge(1, 2) dag:add_edge(2, 3) dag:partial_topo_sort_reset() local node, has_cycle = dag:partial_topo_sort_next() if node then print("Processing node:", node) dag:partial_topo_sort_remove(node) -- This updates in-degrees for nodes dependent on this node end ``` ## graph:find\_cycle * Find cycle in the graph #### Function Prototype ::: tip API ```lua graph:find_cycle() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Searches for a cycle in the graph using depth-first search. Returns an array of vertices that form a cycle, or nil if no cycle exists. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:add_edge(3, 1) -- Creates a cycle local cycle = g:find_cycle() if cycle then print("Found cycle:") for _, v in ipairs(cycle) do print(v) -- Output: 1, 2, 3 (forming a cycle) end end ``` ## graph:clone * Clone the graph #### Function Prototype ::: tip API ```lua graph:clone() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a complete copy of the graph with all vertices and edges. The new graph is independent of the original. ```lua local g1 = graph.new(true) g1:add_edge(1, 2) g1:add_edge(2, 3) local g2 = g1:clone() -- Modifying the copy doesn't affect the original g2:add_edge(3, 4) print(#g1:edges()) -- Output: 2 (original unchanged) print(#g2:edges()) -- Output: 3 (copy modified) ``` ## graph:reverse * Reverse the graph #### Function Prototype ::: tip API ```lua graph:reverse() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a new graph with all edges reversed. For directed graphs, this reverses the direction of all edges. For undirected graphs, this is equivalent to `clone()`. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) local rg = g:reverse() -- Original: 1 -> 2 -> 3 -- Reversed: 1 <- 2 <- 3 print(rg:has_edge(2, 1)) -- Output: true print(rg:has_edge(3, 2)) -- Output: true ``` ## graph:dump * Dump graph information #### Function Prototype ::: tip API ```lua graph:dump() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Prints detailed information about the graph including all vertices and edges. Useful for debugging. ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:dump() -- Output: -- graph: directed, vertices: 3, edges: 2 -- vertices: -- 1 -- 2 -- 3 -- edges: -- 1 -> 2 -- 2 -> 3 ``` ::: tip TIP The graph module is useful for modeling dependencies, scheduling tasks, analyzing relationships, and detecting cycles. It supports both directed and undirected graphs and provides efficient algorithms for common graph operations. ::: ::: warning WARNING * Topological sort only works on directed graphs * Removing a vertex also removes all its associated edges * For undirected graphs, `add_edge(a, b)` creates a bidirectional connection * Partial topological sort supports dynamic graph modifications during the sort ::: --- --- url: /zh/api/scripts/extension-modules/core/base/graph.md --- # graph graph 模块提供了图数据结构,支持有向图和无向图。它包含拓扑排序、环检测和图操作等功能。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.graph")` ::: ## graph.new * 创建新图 #### 函数原型 ::: tip API ```lua graph.new(directed: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | directed | 图是否有向(true)还是无向(false) | #### 用法说明 创建一个新的图对象。`directed` 参数指定图是有向图(true)还是无向图(false)。 ```lua -- 创建有向图(DAG) local dag = graph.new(true) -- 创建无向图 local ug = graph.new(false) ``` ## graph:clear * 清空图 #### 函数原型 ::: tip API ```lua graph:clear() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 删除图中的所有顶点和边,将其重置为空状态。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) print(#g:vertices()) -- 输出: 3 g:clear() print(#g:vertices()) -- 输出: 0 print(g:empty()) -- 输出: true ``` ## graph:empty * 判断图是否为空 #### 函数原型 ::: tip API ```lua graph:empty() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 如果图不包含任何顶点,返回 true。 ```lua local g = graph.new(true) print(g:empty()) -- 输出: true g:add_vertex(1) print(g:empty()) -- 输出: false ``` ## graph:is\_directed * 判断是否为有向图 #### 函数原型 ::: tip API ```lua graph:is_directed() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 如果图是有向图返回 true,无向图返回 false。 ```lua local dag = graph.new(true) print(dag:is_directed()) -- 输出: true local ug = graph.new(false) print(ug:is_directed()) -- 输出: false ``` ## graph:vertices * 获取所有顶点 #### 函数原型 ::: tip API ```lua graph:vertices() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回包含图中所有顶点的数组。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:add_vertex(4) local vertices = g:vertices() for _, v in ipairs(vertices) do print(v) -- 输出: 1, 2, 3, 4 end ``` ## graph:vertex * 获取指定索引的顶点 #### 函数原型 ::: tip API ```lua graph:vertex(idx: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | idx | 顶点索引(从 1 开始) | #### 用法说明 返回指定索引(从 1 开始)的顶点。 ```lua local g = graph.new(true) g:add_edge("a", "b") g:add_edge("b", "c") print(g:vertex(1)) -- 输出: a print(g:vertex(2)) -- 输出: b print(g:vertex(3)) -- 输出: c ``` ## graph:has\_vertex * 判断图中是否存在给定顶点 #### 函数原型 ::: tip API ```lua graph:has_vertex(v: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | v | 要检查的顶点值 | #### 用法说明 如果顶点存在于图中,返回 true。 ```lua local g = graph.new(true) g:add_vertex(1) g:add_vertex(2) print(g:has_vertex(1)) -- 输出: true print(g:has_vertex(3)) -- 输出: false ``` ## graph:add\_vertex * 添加孤立顶点 #### 函数原型 ::: tip API ```lua graph:add_vertex(v: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | v | 要添加的顶点值 | #### 用法说明 向图中添加一个没有边的顶点。如果顶点已存在,此操作无效。 ```lua local g = graph.new(true) g:add_vertex(1) g:add_vertex(2) g:add_vertex(3) print(#g:vertices()) -- 输出: 3 print(#g:edges()) -- 输出: 0 ``` ## graph:remove\_vertex * 删除给定顶点 #### 函数原型 ::: tip API ```lua graph:remove_vertex(v: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | v | 要删除的顶点值 | #### 用法说明 从图中删除顶点及其所有关联的边。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:add_edge(3, 4) g:remove_vertex(2) print(g:has_vertex(2)) -- 输出: false -- 边 1->2 和 2->3 也被删除 ``` ## graph:edges * 获取所有边 #### 函数原型 ::: tip API ```lua graph:edges() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回包含图中所有边的数组。每条边都有 `from()` 和 `to()` 方法。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) for _, e in ipairs(g:edges()) do print(e:from(), "->", e:to()) -- 输出: 1 -> 2 -- 2 -> 3 end ``` ## graph:adjacent\_edges * 获取给定顶点的邻接边 #### 函数原型 ::: tip API ```lua graph:adjacent_edges(v: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | v | 顶点值 | #### 用法说明 返回与指定顶点相邻的边的数组。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(1, 3) g:add_edge(2, 3) local edges = g:adjacent_edges(1) for _, e in ipairs(edges) do print(e:from(), "->", e:to()) -- 输出: 1 -> 2 -- 1 -> 3 end ``` ## graph:add\_edge * 添加边 #### 函数原型 ::: tip API ```lua graph:add_edge(from: , to: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | from | 源顶点 | | to | 目标顶点 | #### 用法说明 添加一条从 `from` 到 `to` 的有向边。对于无向图,这会创建双向连接。如果顶点不存在,会自动创建。 ```lua -- 有向图 local dag = graph.new(true) dag:add_edge("a", "b") dag:add_edge("b", "c") -- 无向图 local ug = graph.new(false) ug:add_edge(1, 2) -- 对于无向图,1->2 和 2->1 都是连通的 ``` ## graph:has\_edge * 判断图中是否存在给定边 #### 函数原型 ::: tip API ```lua graph:has_edge(from: , to: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | from | 源顶点 | | to | 目标顶点 | #### 用法说明 如果存在从 `from` 到 `to` 的边,返回 true。 ```lua local g = graph.new(true) g:add_edge(1, 2) print(g:has_edge(1, 2)) -- 输出: true print(g:has_edge(2, 1)) -- 输出: false (有向图) ``` ## graph:topo\_sort * 执行拓扑排序 #### 函数原型 ::: tip API ```lua graph:topo_sort() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 使用 Kahn 算法对有向图执行拓扑排序。返回按拓扑顺序排列的顶点数组和一个表示是否检测到环的布尔值。仅适用于有向图。 ```lua local dag = graph.new(true) dag:add_edge(0, 5) dag:add_edge(0, 2) dag:add_edge(0, 1) dag:add_edge(3, 6) dag:add_edge(3, 5) dag:add_edge(3, 4) dag:add_edge(5, 4) dag:add_edge(6, 4) dag:add_edge(6, 0) dag:add_edge(3, 2) dag:add_edge(1, 4) local order, has_cycle = dag:topo_sort() if not has_cycle then for _, v in ipairs(order) do print(v) -- 输出: 按拓扑顺序排列的顶点 end else print("图中存在环!") end ``` ::: tip 提示 拓扑排序仅适用于有向无环图(DAG)。如果检测到环,`has_cycle` 标志将为 true。 ::: ## graph:partial\_topo\_sort\_reset * 重置部分拓扑排序状态 #### 函数原型 ::: tip API ```lua graph:partial_topo_sort_reset() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 重置部分拓扑排序的内部状态。在开始新的部分拓扑排序之前必须调用此方法。 ```lua local dag = graph.new(true) dag:add_edge(1, 2) dag:add_edge(2, 3) dag:partial_topo_sort_reset() -- 现在可以进行部分拓扑排序 ``` ## graph:partial\_topo\_sort\_next * 获取拓扑顺序中的下一个节点 #### 函数原型 ::: tip API ```lua graph:partial_topo_sort_next() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回拓扑排序中入度为零的下一个节点。完成或检测到环时返回 nil。`has_cycle` 标志指示是否检测到环。 ```lua local dag = graph.new(true) dag:add_edge("a", "b") dag:add_edge("b", "c") dag:partial_topo_sort_reset() local order_vertices = {} while true do local node, has_cycle = dag:partial_topo_sort_next() if node then table.insert(order_vertices, node) dag:partial_topo_sort_remove(node) else if has_cycle then print("检测到环!") end break end end -- order_vertices = {"a", "b", "c"} ``` ::: tip 提示 部分拓扑排序允许您增量处理节点,并支持在排序过程中动态修改图。 ::: ## graph:partial\_topo\_sort\_remove * 删除节点并更新入度 #### 函数原型 ::: tip API ```lua graph:partial_topo_sort_remove(node: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | node | 要从排序中删除的节点 | #### 用法说明 从部分拓扑排序中删除指定节点,并更新其依赖节点的入度。应在处理完 `partial_topo_sort_next()` 返回的每个节点后调用此方法。 ```lua local dag = graph.new(true) dag:add_edge(1, 2) dag:add_edge(2, 3) dag:partial_topo_sort_reset() local node, has_cycle = dag:partial_topo_sort_next() if node then print("处理节点:", node) dag:partial_topo_sort_remove(node) -- 这会更新依赖此节点的节点的入度 end ``` ## graph:find\_cycle * 查找图中的环 #### 函数原型 ::: tip API ```lua graph:find_cycle() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 使用深度优先搜索在图中查找环。返回构成环的顶点数组,如果不存在环则返回 nil。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:add_edge(3, 1) -- 创建一个环 local cycle = g:find_cycle() if cycle then print("找到环:") for _, v in ipairs(cycle) do print(v) -- 输出: 1, 2, 3 (形成一个环) end end ``` ## graph:clone * 克隆图 #### 函数原型 ::: tip API ```lua graph:clone() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建图的完整副本,包含所有顶点和边。新图与原图独立。 ```lua local g1 = graph.new(true) g1:add_edge(1, 2) g1:add_edge(2, 3) local g2 = g1:clone() -- 修改副本不影响原图 g2:add_edge(3, 4) print(#g1:edges()) -- 输出: 2 (原图不变) print(#g2:edges()) -- 输出: 3 (副本已修改) ``` ## graph:reverse * 反转图 #### 函数原型 ::: tip API ```lua graph:reverse() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建一个所有边都反转的新图。对于有向图,这会反转所有边的方向。对于无向图,这等同于 `clone()`。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) local rg = g:reverse() -- 原图: 1 -> 2 -> 3 -- 反转: 1 <- 2 <- 3 print(rg:has_edge(2, 1)) -- 输出: true print(rg:has_edge(3, 2)) -- 输出: true ``` ## graph:dump * 输出图信息 #### 函数原型 ::: tip API ```lua graph:dump() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 打印图的详细信息,包括所有顶点和边。用于调试。 ```lua local g = graph.new(true) g:add_edge(1, 2) g:add_edge(2, 3) g:dump() -- 输出: -- graph: directed, vertices: 3, edges: 2 -- vertices: -- 1 -- 2 -- 3 -- edges: -- 1 -> 2 -- 2 -> 3 ``` ::: tip 提示 graph 模块适用于建模依赖关系、调度任务、分析关系和检测环。它支持有向图和无向图,并为常见的图操作提供了高效的算法。 ::: ::: warning 注意 * 拓扑排序仅适用于有向图 * 删除顶点也会删除其所有关联的边 * 对于无向图,`add_edge(a, b)` 会创建双向连接 * 部分拓扑排序支持在排序过程中动态修改图 ::: --- --- url: /api/scripts/builtin-modules/hash.md --- # hash The hash module provides hash value calculation and UUID generation functions. It is a built-in module of xmake. ## hash.md5 * Calculate the MD5 hash value of a string or file #### Function Prototype ::: tip API ```lua hash.md5(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String or file path | #### Usage ```lua local hashval = hash.md5("hello") local hashval = hash.md5("/path/to/file") ``` Calculates the MD5 hash value of the specified string or file and returns a hexadecimal format hash string. Supports both string and file path as input. Commonly used for calculating file content checksums: ```lua -- Read file content and calculate MD5 local content = io.readfile("file.txt") local checksum = hash.md5(content) print("MD5: " .. checksum) ``` ## hash.sha1 * Calculate the SHA1 hash value of a string or file #### Function Prototype ::: tip API ```lua hash.sha1(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String or file path | #### Usage ```lua local hashval = hash.sha1("hello") local hashval = hash.sha1("/path/to/file") ``` Calculates the SHA1 hash value of the specified string or file and returns a hexadecimal format hash string. ## hash.sha256 * Calculate the SHA256 hash value of a string or file #### Function Prototype ::: tip API ```lua hash.sha256(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String or file path | #### Usage ```lua local hashval = hash.sha256("hello") local hashval = hash.sha256("/path/to/file") ``` Calculates the SHA256 hash value of the specified string or file and returns a hexadecimal format hash string. SHA256 is more secure than MD5 and is commonly used for package integrity verification: ```lua -- Verify downloaded package file local packagefile = "package.tar.gz" local checksum = hash.sha256(packagefile) if checksum ~= expected_hash then raise("checksum mismatch!") end ``` ## hash.uuid * Generate a UUID based on a name #### Function Prototype ::: tip API ```lua hash.uuid(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Name string for UUID generation | #### Usage ```lua local id = hash.uuid("name") ``` Generates a deterministic UUID based on the given name string. The same name always generates the same UUID. Internally calls `hash.uuid4(str)`, format: `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` Suitable for generating fixed unique identifiers for specific configurations: ```lua -- Generate deterministic IDs for different build configurations local config_id = hash.uuid("debug-x64-windows") ``` ## hash.xxhash32 * Calculate the 32-bit xxHash hash value of a string or file #### Function Prototype ::: tip API ```lua hash.xxhash32(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String or file path | #### Usage Calculates hash value using the xxHash32 algorithm. xxHash is an extremely fast non-cryptographic hash algorithm suitable for hash tables, checksums, and other scenarios. ## hash.xxhash64 * Calculate the 64-bit xxHash hash value of a string or file #### Function Prototype ::: tip API ```lua hash.xxhash64(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String or file path | #### Usage Calculates hash value using the xxHash64 algorithm. Fast and suitable for quick verification: ```lua -- Generate fast hash key for compilation parameters local key = hash.xxhash64(table.concat(params, "|")) ``` ## hash.xxhash128 * Calculate the 128-bit xxHash hash value of a string or file #### Function Prototype ::: tip API ```lua hash.xxhash128(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String or file path | #### Usage Calculates hash value using the xxHash128 algorithm, providing longer hash values to reduce collisions. ## hash.strhash32 * Generate a 32-bit hash value from a string #### Function Prototype ::: tip API ```lua hash.strhash32(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String | #### Usage Generates a 32-bit hash value from a string, returns format like: `91e8ecf1` This interface uses xxhash32 internally, specifically designed for fast string hashing. ## hash.strhash64 * Generate a 64-bit hash value from a string #### Function Prototype ::: tip API ```lua hash.strhash64(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String | #### Usage Generates a 64-bit hash value from a string, returns format like: `91e8ecf191e8ecf1` ## hash.strhash128 * Generate a 128-bit hash value from a string #### Function Prototype ::: tip API ```lua hash.strhash128(input: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | input | String | #### Usage Generates a 128-bit hash value from a string, returns format like: `91e8ecf1417f4edfa574e22d7d8d204a` Suitable for generating compilation cache keys: ```lua -- Generate key for compilation cache local cache_key = hash.strhash128(compiler .. flags .. source) ``` ## hash.rand32 * Generate a 32-bit random hash value #### Function Prototype ::: tip API ```lua hash.rand32() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Generates a 32-bit random hash value. ::: warning WARNING This interface is prone to hash collisions and is not recommended for scenarios requiring high uniqueness. ::: ## hash.rand64 * Generate a 64-bit random hash value #### Function Prototype ::: tip API ```lua hash.rand64() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Generates a 64-bit random hash value. ## hash.rand128 * Generate a 128-bit random hash value #### Function Prototype ::: tip API ```lua hash.rand128() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Generates a 128-bit random hash value. --- --- url: /zh/api/scripts/builtin-modules/hash.md --- # hash hash 模块提供了哈希值计算和 UUID 生成功能,这是 xmake 的一个内置模块。 ## hash.md5 * 计算字符串或文件的 MD5 哈希值 #### 函数原型 ::: tip API ```lua hash.md5(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串或文件路径 | #### 用法说明 计算指定字符串或文件的 MD5 哈希值,返回十六进制格式的哈希字符串。支持传入字符串或文件路径。 通常用于计算文件内容校验和: ```lua -- 读取文件内容并计算 MD5 local content = io.readfile("file.txt") local checksum = hash.md5(content) print("MD5: " .. checksum) ``` ## hash.sha1 * 计算字符串或文件的 SHA1 哈希值 #### 函数原型 ::: tip API ```lua hash.sha1(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串或文件路径 | #### 用法说明 计算指定字符串或文件的 SHA1 哈希值,返回十六进制格式的哈希字符串。 ## hash.sha256 * 计算字符串或文件的 SHA256 哈希值 #### 函数原型 ::: tip API ```lua hash.sha256(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串或文件路径 | #### 用法说明 计算指定字符串或文件的 SHA256 哈希值,返回十六进制格式的哈希字符串。 SHA256 比 MD5 更安全,常用于包的完整性校验: ```lua -- 校验下载的包文件 local packagefile = "package.tar.gz" local checksum = hash.sha256(packagefile) if checksum ~= expected_hash then raise("checksum mismatch!") end ``` ## hash.uuid * 根据名称生成 UUID #### 函数原型 ::: tip API ```lua hash.uuid(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 名称字符串 | #### 用法说明 根据给定的名称字符串生成一个确定性的 UUID,相同的名称总是生成相同的 UUID。 内部调用 `hash.uuid4(str)`,格式为:`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` 适合为特定配置生成固定的唯一标识符: ```lua -- 为不同的编译配置生成确定性 ID local config_id = hash.uuid("debug-x64-windows") ``` ## hash.xxhash32 * 计算字符串或文件的 32 位 xxHash 哈希值 #### 函数原型 ::: tip API ```lua hash.xxhash32(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串或文件路径 | #### 用法说明 使用 xxHash32 算法计算哈希值,xxHash 是一个极快的非加密哈希算法,适合用于哈希表、校验和等场景。 ## hash.xxhash64 * 计算字符串或文件的 64 位 xxHash 哈希值 #### 函数原型 ::: tip API ```lua hash.xxhash64(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串或文件路径 | #### 用法说明 使用 xxHash64 算法计算哈希值,速度快,适合快速校验: ```lua -- 为编译参数生成快速哈希键 local key = hash.xxhash64(table.concat(params, "|")) ``` ## hash.xxhash128 * 计算字符串或文件的 128 位 xxHash 哈希值 #### 函数原型 ::: tip API ```lua hash.xxhash128(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串或文件路径 | #### 用法说明 使用 xxHash128 算法计算哈希值,提供更长的哈希值以减少冲突。 ## hash.strhash32 * 从字符串生成 32 位哈希值 #### 函数原型 ::: tip API ```lua hash.strhash32(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串 | #### 用法说明 从字符串生成 32 位哈希值,返回格式如:`91e8ecf1` 这个接口内部使用 xxhash32,专门用于字符串快速哈希。 ## hash.strhash64 * 从字符串生成 64 位哈希值 #### 函数原型 ::: tip API ```lua hash.strhash64(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串 | #### 用法说明 从字符串生成 64 位哈希值,返回格式如:`91e8ecf191e8ecf1` ## hash.strhash128 * 从字符串生成 128 位哈希值 #### 函数原型 ::: tip API ```lua hash.strhash128(input: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | input | 字符串 | #### 用法说明 从字符串生成 128 位哈希值,返回格式如:`91e8ecf1417f4edfa574e22d7d8d204a` 适合用于生成编译缓存键: ```lua -- 为编译缓存生成键 local cache_key = hash.strhash128(compiler .. flags .. source) ``` ## hash.rand32 * 生成 32 位随机哈希值 #### 函数原型 ::: tip API ```lua hash.rand32() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 生成一个 32 位的随机哈希值。 ::: warning 注意 此接口容易触发哈希冲突,不建议用于需要高唯一性的场景。 ::: ## hash.rand64 * 生成 64 位随机哈希值 #### 函数原型 ::: tip API ```lua hash.rand64() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 生成一个 64 位的随机哈希值。 ## hash.rand128 * 生成 128 位随机哈希值 #### 函数原型 ::: tip API ```lua hash.rand128() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 生成一个 128 位的随机哈希值。 --- --- url: /api/scripts/extension-modules/core/base/hashset.md --- # hashset The hashset module provides a hash set data structure (a collection with no duplicate elements) for efficiently storing and querying unique values. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.hashset")` ::: ## hashset.new * Create an empty hash set #### Function Prototype ::: tip API ```lua hashset.new() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates an empty hash set object. ```lua local set = hashset.new() print(set:size()) -- Output: 0 print(set:empty()) -- Output: true ``` ## hashset.of * Create hash set from parameter list #### Function Prototype ::: tip API ```lua hashset.of(...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ... | Variable number of values to initialize the set | #### Usage Creates a hash set from a parameter list, automatically removing duplicate elements. This is a convenient way to create and initialize a set: ```lua local set = hashset.of(1, 2, 3, 5, 5, 7, 1, 9, 4, 6, 8, 0) print(set:size()) -- Output: 10 (duplicates 1 and 5 removed) -- Verify elements assert(set:has(1)) assert(set:has(5)) assert(not set:has(10)) ``` ## hashset.from * Create hash set from array #### Function Prototype ::: tip API ```lua hashset.from(array:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | array | Required. Array to create the set from | #### Usage Creates a hash set from an array, automatically removing duplicate elements. Used for deduplicating arrays: ```lua local array = {1, 2, 3, 2, 4, 3, 5} local set = hashset.from(array) print(set:size()) -- Output: 5 -- Convert back to array (deduplicated) local unique_array = set:to_array() ``` ## hashset:insert * Insert an element #### Function Prototype ::: tip API ```lua hashset:insert(value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | value | Required. Element to insert | #### Return Value | Type | Description | |------|-------------| | true | Element doesn't exist, insertion successful | | false | Element already exists, not inserted | #### Usage Inserts an element into the hash set. If the element already exists, it will not be inserted. ```lua local set = hashset.new() local result = set:insert(1) print(result) -- Output: true (insertion successful) local result = set:insert(1) print(result) -- Output: false (element already exists) print(set:size()) -- Output: 1 ``` Supports inserting various types of values, including strings, numbers, tables, nil, etc.: ```lua local set = hashset.new() set:insert("hello") set:insert(123) set:insert({key = "value"}) set:insert(nil) -- Can also insert nil value ``` ## hashset:remove * Remove an element #### Function Prototype ::: tip API ```lua hashset:remove(value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | value | Required. Element to remove | #### Return Value | Type | Description | |------|-------------| | true | Element exists, removal successful | | false | Element doesn't exist, not removed | #### Usage Removes an element from the hash set. ```lua local set = hashset.of(1, 2, 3) local result = set:remove(2) print(result) -- Output: true (removal successful) print(set:size()) -- Output: 2 local result = set:remove(10) print(result) -- Output: false (element doesn't exist) ``` ## hashset:has * Check if element exists #### Function Prototype ::: tip API ```lua hashset:has(value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | value | Required. Element to check | #### Return Value | Type | Description | |------|-------------| | true | Element exists | | false | Element doesn't exist | #### Usage Checks if the specified element is in the hash set. Used for fast element lookup (O(1) time complexity): ```lua local set = hashset.of(1, 2, 3, 4, 5) if set:has(3) then print("Set contains 3") end if not set:has(10) then print("Set doesn't contain 10") end ``` ## hashset:size * Get set size #### Function Prototype ::: tip API ```lua hashset:size() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the number of elements in the hash set. ```lua local set = hashset.of(1, 2, 3, 4, 5) print(set:size()) -- Output: 5 set:insert(6) print(set:size()) -- Output: 6 set:remove(1) print(set:size()) -- Output: 5 ``` ## hashset:empty * Check if set is empty #### Function Prototype ::: tip API ```lua hashset:empty() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns true if the set is empty (contains no elements). ```lua local set = hashset.new() print(set:empty()) -- Output: true set:insert(1) print(set:empty()) -- Output: false ``` ## hashset:clear * Clear the set #### Function Prototype ::: tip API ```lua hashset:clear() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes all elements from the set, resetting it to an empty set. ```lua local set = hashset.of(1, 2, 3, 4, 5) print(set:size()) -- Output: 5 set:clear() print(set:size()) -- Output: 0 print(set:empty()) -- Output: true ``` ## hashset:clone * Clone the set #### Function Prototype ::: tip API ```lua hashset:clone() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a complete copy of the hash set, with the new set being independent of the original. Used for saving snapshots of the set or creating copies: ```lua local set1 = hashset.of(1, 2, 3) local set2 = set1:clone() -- Modifying the copy doesn't affect the original set2:insert(4) print(set1:size()) -- Output: 3 print(set2:size()) -- Output: 4 -- Compare sets for equality set2:remove(4) assert(set1 == set2) -- Equal ``` ## hashset:to\_array * Convert to array #### Function Prototype ::: tip API ```lua hashset:to_array() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Converts the hash set to an array, returning a table containing all elements. nil values are ignored. Commonly used for array deduplication: ```lua local array = {1, 2, 3, 2, 4, 3, 5, 1} local set = hashset.from(array) local unique_array = set:to_array() -- unique_array contains: {1, 2, 3, 4, 5} (order may vary) print("Original array size:", #array) -- 8 print("Deduplicated size:", #unique_array) -- 5 ``` ## hashset:items * Iterate over set elements #### Function Prototype ::: tip API ```lua hashset:items() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an iterator function for traversing all elements in the set (unordered). ```lua local set = hashset.of("apple", "banana", "orange") for item in set:items() do print(item) end -- Output order is indeterminate: might be apple, orange, banana ``` Used for checking all elements in the set: ```lua local set = hashset.of(1, 2, 3, 4, 5) -- Check all elements for item in set:items() do assert(set:has(item)) end -- Calculate sum local sum = 0 for item in set:items() do sum = sum + item end print("Sum:", sum) -- Output: 15 ``` ## hashset:orderitems * Iterate over set elements in order #### Function Prototype ::: tip API ```lua hashset:orderitems() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an iterator function for traversing all elements in the set in ascending order. Suitable for scenarios requiring ordered output: ```lua local set = hashset.of(5, 2, 8, 1, 9, 3) print("Unordered iteration:") for item in set:items() do print(item) -- Order is indeterminate end print("Ordered iteration:") for item in set:orderitems() do print(item) -- Output: 1, 2, 3, 5, 8, 9 end ``` Verify ordering: ```lua local set = hashset.of(9, 1, 5, 3, 7, 2, 8, 4, 6, 0) local prev = -1 for item in set:orderitems() do assert(item > prev) -- Each element is greater than the previous prev = item end ``` hashset also supports comparing two sets for equality using the `==` operator (containing the same elements): ```lua local set1 = hashset.of(1, 2, 3) local set2 = hashset.of(3, 2, 1) local set3 = hashset.of(1, 2, 4) assert(set1 == set2) -- true (same elements, order irrelevant) assert(not (set1 == set3)) -- false (different elements) ``` ::: tip TIP hashset provides O(1) time complexity for insert, delete, and lookup operations, much more efficient than linear search using arrays. Suitable for scenarios requiring frequent element existence checks or deduplication. ::: ::: warning WARNING * Elements in hashset are unordered; order is indeterminate when iterating with `items()` * Use `orderitems()` for ordered iteration * hashset automatically removes duplicate elements * nil values can be stored in hashset ::: --- --- url: /zh/api/scripts/extension-modules/core/base/hashset.md --- # hashset hashset 模块提供了哈希集合(无重复元素的集合)数据结构,用于高效地存储和查询唯一值。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.hashset")` ::: ## hashset.new * 创建空的哈希集合 #### 函数原型 ::: tip API ```lua hashset.new() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建一个空的哈希集合对象。 ```lua local set = hashset.new() print(set:size()) -- 输出: 0 print(set:empty()) -- 输出: true ``` ## hashset.of * 从参数列表创建哈希集合 #### 函数原型 ::: tip API ```lua hashset.of(...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ... | 可变参数,要添加到集合中的元素 | #### 用法说明 从参数列表创建哈希集合,自动去除重复元素。 这是创建并初始化集合的便捷方式: ```lua local set = hashset.of(1, 2, 3, 5, 5, 7, 1, 9, 4, 6, 8, 0) print(set:size()) -- 输出: 10 (重复的 1 和 5 被去除) -- 验证元素 assert(set:has(1)) assert(set:has(5)) assert(not set:has(10)) ``` ## hashset.from * 从数组创建哈希集合 #### 函数原型 ::: tip API ```lua hashset.from(array:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | array | 要转换为集合的数组 | #### 用法说明 从数组创建哈希集合,自动去除重复元素。 用于将数组去重: ```lua local array = {1, 2, 3, 2, 4, 3, 5} local set = hashset.from(array) print(set:size()) -- 输出: 5 -- 转回数组(已去重) local unique_array = set:to_array() ``` ## hashset:insert * 插入元素 #### 函数原型 ::: tip API ```lua hashset:insert(value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | value | 要插入的元素值 | #### 返回值说明 | 类型 | 描述 | |------|------| | true | 元素不存在,插入成功 | | false | 元素已存在,未插入 | #### 用法说明 向哈希集合插入一个元素。如果元素已存在,则不插入。 ```lua local set = hashset.new() local result = set:insert(1) print(result) -- 输出: true (插入成功) local result = set:insert(1) print(result) -- 输出: false (元素已存在) print(set:size()) -- 输出: 1 ``` 支持插入各种类型的值,包括字符串、数字、table、nil 等: ```lua local set = hashset.new() set:insert("hello") set:insert(123) set:insert({key = "value"}) set:insert(nil) -- 也可以插入 nil 值 ``` ## hashset:remove * 删除元素 #### 函数原型 ::: tip API ```lua hashset:remove(value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | value | 要删除的元素值 | #### 返回值说明 | 类型 | 描述 | |------|------| | true | 元素存在,删除成功 | | false | 元素不存在,未删除 | #### 用法说明 从哈希集合中删除一个元素。 ```lua local set = hashset.of(1, 2, 3) local result = set:remove(2) print(result) -- 输出: true (删除成功) print(set:size()) -- 输出: 2 local result = set:remove(10) print(result) -- 输出: false (元素不存在) ``` ## hashset:has * 检查元素是否存在 #### 函数原型 ::: tip API ```lua hashset:has(value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | value | 要检查的元素值 | #### 返回值说明 | 类型 | 描述 | |------|------| | true | 元素存在 | | false | 元素不存在 | #### 用法说明 检查指定元素是否在哈希集合中。 用于快速查找元素(O(1) 时间复杂度): ```lua local set = hashset.of(1, 2, 3, 4, 5) if set:has(3) then print("集合包含 3") end if not set:has(10) then print("集合不包含 10") end ``` ## hashset:size * 获取集合大小 #### 函数原型 ::: tip API ```lua hashset:size() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回哈希集合中元素的个数。 ```lua local set = hashset.of(1, 2, 3, 4, 5) print(set:size()) -- 输出: 5 set:insert(6) print(set:size()) -- 输出: 6 set:remove(1) print(set:size()) -- 输出: 5 ``` ## hashset:empty * 判断集合是否为空 #### 函数原型 ::: tip API ```lua hashset:empty() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回 true 表示集合为空(不包含任何元素)。 ```lua local set = hashset.new() print(set:empty()) -- 输出: true set:insert(1) print(set:empty()) -- 输出: false ``` ## hashset:clear * 清空集合 #### 函数原型 ::: tip API ```lua hashset:clear() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 删除集合中的所有元素,重置为空集合。 ```lua local set = hashset.of(1, 2, 3, 4, 5) print(set:size()) -- 输出: 5 set:clear() print(set:size()) -- 输出: 0 print(set:empty()) -- 输出: true ``` ## hashset:clone * 克隆集合 #### 函数原型 ::: tip API ```lua hashset:clone() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建哈希集合的完整副本,新集合与原集合独立。 用于保存集合的快照或创建副本: ```lua local set1 = hashset.of(1, 2, 3) local set2 = set1:clone() -- 修改副本不影响原集合 set2:insert(4) print(set1:size()) -- 输出: 3 print(set2:size()) -- 输出: 4 -- 比较集合是否相等 set2:remove(4) assert(set1 == set2) -- 相等 ``` ## hashset:to\_array * 转换为数组 #### 函数原型 ::: tip API ```lua hashset:to_array() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 将哈希集合转换为数组,返回包含所有元素的 table。nil 值会被忽略。 常用于数组去重: ```lua local array = {1, 2, 3, 2, 4, 3, 5, 1} local set = hashset.from(array) local unique_array = set:to_array() -- unique_array 包含: {1, 2, 3, 4, 5} (顺序可能不同) print("原数组大小:", #array) -- 8 print("去重后大小:", #unique_array) -- 5 ``` ## hashset:items * 遍历集合元素 #### 函数原型 ::: tip API ```lua hashset:items() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回一个迭代器函数,用于遍历集合中的所有元素(无序)。 ```lua local set = hashset.of("apple", "banana", "orange") for item in set:items() do print(item) end -- 输出顺序不确定:可能是 apple, orange, banana ``` 用于检查集合中的所有元素: ```lua local set = hashset.of(1, 2, 3, 4, 5) -- 检查所有元素 for item in set:items() do assert(set:has(item)) end -- 计算总和 local sum = 0 for item in set:items() do sum = sum + item end print("总和:", sum) -- 输出: 15 ``` ## hashset:orderitems * 按序遍历集合元素 #### 函数原型 ::: tip API ```lua hashset:orderitems() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回一个迭代器函数,用于按升序遍历集合中的所有元素。 适合需要有序输出的场景: ```lua local set = hashset.of(5, 2, 8, 1, 9, 3) print("无序遍历:") for item in set:items() do print(item) -- 顺序不确定 end print("有序遍历:") for item in set:orderitems() do print(item) -- 输出: 1, 2, 3, 5, 8, 9 end ``` 验证顺序性: ```lua local set = hashset.of(9, 1, 5, 3, 7, 2, 8, 4, 6, 0) local prev = -1 for item in set:orderitems() do assert(item > prev) -- 每个元素都大于前一个 prev = item end ``` hashset 还支持通过 `==` 运算符比较两个集合是否相等(包含相同的元素): ```lua local set1 = hashset.of(1, 2, 3) local set2 = hashset.of(3, 2, 1) local set3 = hashset.of(1, 2, 4) assert(set1 == set2) -- true (元素相同,顺序无关) assert(not (set1 == set3)) -- false (元素不同) ``` ::: tip 提示 hashset 提供了 O(1) 时间复杂度的插入、删除和查找操作,比使用数组进行线性查找效率高得多。适合需要频繁检查元素存在性或去重的场景。 ::: ::: warning 注意 * hashset 中的元素是无序的,使用 `items()` 遍历时顺序不确定 * 如需有序遍历,使用 `orderitems()` * hashset 会自动去除重复元素 * nil 值可以被存储在 hashset 中 ::: --- --- url: /api/scripts/extension-modules/core/base/heap.md --- # heap The heap module provides priority queue data structures implemented as binary heaps. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.heap")` ::: ## heap.valueheap * Create a value heap #### Function Prototype ::: tip API ```lua heap.valueheap(options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | options | Optional. Lua table with options | Parameters in `options`: * `cmp` - Optional comparison function. Should return true if the first argument has higher priority than the second #### Usage Creates a new value heap using a Lua table. The heap is a min-heap by default, but can be customized with a comparison function. ```lua -- Create a min-heap (default) local h = heap.valueheap() -- Create a max-heap local h = heap.valueheap{ cmp = function(a, b) return a > b end } -- Create a priority queue with custom objects local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } ``` Initialize with existing values: ```lua -- Create heap with initial values local h = heap.valueheap{1, 5, 3, 7, 2} ``` ## heap:push * Push a value onto the heap #### Function Prototype ::: tip API ```lua heap:push(value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | value | Required. Value to push onto the heap | #### Usage Adds a value to the heap and maintains the heap property. The value will be placed in the correct position according to the comparison function. ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) h:push(3) print(h:peek()) -- Output: 3 (smallest value) ``` Push custom objects: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 20, data = 'low'} h:push{priority = 10, data = 'high'} h:push{priority = 15, data = 'medium'} print(h:peek().data) -- Output: high (priority 10) ``` ::: tip TIP The value cannot be nil. ::: ## heap:pop * Pop a value from the heap #### Function Prototype ::: tip API ```lua heap:pop(index: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | index | Optional. The position to pop from (1-based). Defaults to 1 (top of heap) | #### Usage Removes and returns a value from the heap. If no index is provided, removes the top element (highest priority). After removal, the heap property is maintained. ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) local v1 = h:pop() -- Returns 5 (smallest) local v2 = h:pop() -- Returns 10 local v3 = h:pop() -- Returns 20 ``` Pop with custom priority: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 20, data = 'bar'} h:push{priority = 10, data = 'foo'} local item = h:pop() print(item.priority) -- Output: 10 print(item.data) -- Output: foo ``` ## heap:peek * Peek at a value without removing it #### Function Prototype ::: tip API ```lua heap:peek(index: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | index | Optional. The position to peek at (1-based). Defaults to 1 (top of heap) | #### Usage Returns a value from the heap without removing it. If no index is provided, returns the top element (highest priority). ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) print(h:peek()) -- Output: 5 (smallest) print(h:peek()) -- Output: 5 (still there) local v = h:pop() print(h:peek()) -- Output: 10 (next smallest) ``` Check next task without processing: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 10, task = 'urgent'} h:push{priority = 20, task = 'normal'} if h:peek().priority < 15 then print("Processing urgent task") local task = h:pop() end ``` ## heap:replace * Replace a value at a given index #### Function Prototype ::: tip API ```lua heap:replace(index: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | index | Required. The position to replace (1-based) | | value | Required. The new value | #### Usage Replaces the value at the specified index with a new value and rebalances the heap to maintain the heap property. ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) -- Replace the top element h:replace(1, 15) print(h:pop()) -- Output: 10 print(h:pop()) -- Output: 15 print(h:pop()) -- Output: 20 ``` Update priority in a priority queue: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 10, id = 1} h:push{priority = 20, id = 2} h:push{priority = 15, id = 3} -- Update priority of element at index 2 h:replace(2, {priority = 5, id = 2}) print(h:pop().id) -- Output: 2 (now has highest priority) ``` ::: tip TIP Use `replace` when you need to update an element's priority without removing and re-adding it, which is more efficient. ::: ## heap.length * Get the number of elements in the heap #### Function Prototype ::: tip API ```lua heap:length() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the current number of elements in the heap. This is a function, not a method. ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) print(h.length()) -- Output: 3 h:pop() print(h.length()) -- Output: 2 ``` Check if heap is empty: ```lua local h = heap.valueheap() if h.length() == 0 then print("Heap is empty") end h:push(10) if h.length() > 0 then print("Heap has", h.length(), "elements") end ``` Process all elements: ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) while h.length() > 0 do local v = h:pop() print(v) -- Output: 5, 10, 20 end ``` ::: tip TIP The heap module implements a binary heap (priority queue) data structure. By default, it's a min-heap where the smallest element has the highest priority. You can customize the comparison function to create max-heaps or heaps based on custom priority criteria. Heap operations (push, pop, peek, replace) all have O(log n) time complexity. ::: ::: warning WARNING * Pushed values cannot be nil * Index is 1-based (Lua convention) * Calling `pop()` on an empty heap will cause an error * `length()` is a function, not a property, so use `h.length()` not `h.length` ::: --- --- url: /zh/api/scripts/extension-modules/core/base/heap.md --- # heap heap 模块提供了使用二叉堆实现的优先队列数据结构。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.heap")` ::: ## heap.valueheap * 创建值堆 #### 函数原型 ::: tip API ```lua heap.valueheap(options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | options | 可选的配置表,包含比较函数等选项 | #### 用法说明 使用 Lua 表创建一个新的值堆。默认情况下堆是最小堆,但可以使用比较函数进行自定义。 `options` 中的参数: * `cmp` - 可选的比较函数。如果第一个参数的优先级高于第二个参数,应返回 true ```lua -- 创建最小堆(默认) local h = heap.valueheap() -- 创建最大堆 local h = heap.valueheap{ cmp = function(a, b) return a > b end } -- 创建自定义对象的优先队列 local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } ``` 使用现有值初始化: ```lua -- 使用初始值创建堆 local h = heap.valueheap{1, 5, 3, 7, 2} ``` ## heap:push * 向堆中推入一个值 #### 函数原型 ::: tip API ```lua heap:push(value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | value | 要推入堆中的值(不能为 nil) | #### 用法说明 向堆中添加一个值并维护堆属性。该值将根据比较函数放置在正确的位置。 ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) h:push(3) print(h:peek()) -- 输出: 3 (最小值) ``` 推入自定义对象: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 20, data = 'low'} h:push{priority = 10, data = 'high'} h:push{priority = 15, data = 'medium'} print(h:peek().data) -- 输出: high (优先级 10) ``` ::: tip 提示 值不能为 nil。 ::: ## heap:pop * 从堆中弹出一个值 #### 函数原型 ::: tip API ```lua heap:pop(index: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | index | 可选。要弹出的位置(从 1 开始)。默认为 1(堆顶) | #### 用法说明 从堆中移除并返回一个值。如果未提供索引,则移除顶部元素(最高优先级)。移除后,堆属性将得到维护。 ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) local v1 = h:pop() -- 返回 5 (最小) local v2 = h:pop() -- 返回 10 local v3 = h:pop() -- 返回 20 ``` 使用自定义优先级弹出: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 20, data = 'bar'} h:push{priority = 10, data = 'foo'} local item = h:pop() print(item.priority) -- 输出: 10 print(item.data) -- 输出: foo ``` ## heap:peek * 查看值而不移除它 #### 函数原型 ::: tip API ```lua heap:peek(index: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | index | 可选。要查看的位置(从 1 开始)。默认为 1(堆顶) | #### 用法说明 从堆中返回一个值而不移除它。如果未提供索引,则返回顶部元素(最高优先级)。 ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) print(h:peek()) -- 输出: 5 (最小) print(h:peek()) -- 输出: 5 (仍然存在) local v = h:pop() print(h:peek()) -- 输出: 10 (下一个最小) ``` 检查下一个任务而不处理: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 10, task = 'urgent'} h:push{priority = 20, task = 'normal'} if h:peek().priority < 15 then print("处理紧急任务") local task = h:pop() end ``` ## heap:replace * 替换给定索引处的值 #### 函数原型 ::: tip API ```lua heap:replace(index: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | index | 必需。要替换的位置(从 1 开始) | | value | 必需。新值 | #### 用法说明 将指定索引处的值替换为新值,并重新平衡堆以维护堆属性。 ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) -- 替换顶部元素 h:replace(1, 15) print(h:pop()) -- 输出: 10 print(h:pop()) -- 输出: 15 print(h:pop()) -- 输出: 20 ``` 在优先队列中更新优先级: ```lua local h = heap.valueheap{ cmp = function(a, b) return a.priority < b.priority end } h:push{priority = 10, id = 1} h:push{priority = 20, id = 2} h:push{priority = 15, id = 3} -- 更新索引 2 处元素的优先级 h:replace(2, {priority = 5, id = 2}) print(h:pop().id) -- 输出: 2 (现在具有最高优先级) ``` ::: tip 提示 当需要更新元素的优先级而不删除和重新添加它时,使用 `replace`,这样更高效。 ::: ## heap.length * 获取堆中的元素数量 #### 函数原型 ::: tip API ```lua heap.length() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回堆中当前的元素数量。这是一个函数,不是方法。 ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) print(h.length()) -- 输出: 3 h:pop() print(h.length()) -- 输出: 2 ``` 检查堆是否为空: ```lua local h = heap.valueheap() if h.length() == 0 then print("堆为空") end h:push(10) if h.length() > 0 then print("堆有", h.length(), "个元素") end ``` 处理所有元素: ```lua local h = heap.valueheap() h:push(10) h:push(5) h:push(20) while h.length() > 0 do local v = h:pop() print(v) -- 输出: 5, 10, 20 end ``` ::: tip 提示 heap 模块实现了二叉堆(优先队列)数据结构。默认情况下,它是一个最小堆,其中最小的元素具有最高优先级。您可以自定义比较函数来创建最大堆或基于自定义优先级标准的堆。堆操作(push、pop、peek、replace)都具有 O(log n) 时间复杂度。 ::: ::: warning 注意 * 推入的值不能为 nil * 索引从 1 开始(Lua 约定) * 在空堆上调用 `pop()` 会导致错误 * `length()` 是一个函数,不是属性,所以使用 `h.length()` 而不是 `h.length` ::: --- --- url: /api/description/helper-interfaces.md --- # Helper Interfaces In addition, the version of this interface after 2.2.5 provides some built-in helper functions, which can be imported directly using includes. See the specific built-in functions: \[Helper functions]\(https://github.com/xmake- io/xmake/tree/master/xmake/includes) We can use these interfaces to detect links, c/c++ type, includes and compiler features, and write macro definitions to config.h Among them, we provide two types of interfaces, `check_xxx` and `configvar_check_xxx`. The interfaces prefixed with `configvar_` will be written into the config.h.in template file specified by `add_configfiles` after passing the test. And `check_xxx` only defines related macros to participate in compilation, but it will not be persisted in `config.h.in`. For related issues, see: -[#342](https://github.com/xmake-io/xmake/issues/342) -[#1715](https://github.com/xmake-io/xmake/issues/1715) Note: Instead of introducing these interfaces separately via `includes("check_links.lua")`, for versions 2.8.5 and above, you can use the more convenient ```lua includes("@builtin/check") ``` to bring in all the checking interfaces at once, but of course we can also bring in individual scripts on an as-needed basis: ```lua includes("@builtin/check/check_links.lua") ``` The original introduction path, which does not distinguish whether it is a user path or not, is not easy to manage and maintain, and is easily interfered by user configurations, so it will be gradually deprecated later. ## Check links We can check whether the specified links pass or not by trying to link. ```lua includes("check_links.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_links("HAS_PTHREAD", {"pthread", "m", "dl"}) ``` ```c [config.h.in] ${define HAS_PTHREAD} ``` ```c [config.h] define HAS_PTHREAD 1 /* #undef HAS_PTHREAD */ ``` ## Detect c/c++ type We can also detect the existence of c/c++ types. `configvar_check_ctypes` is used to detect c code types, and `configvar_check_cxxtypes` is used to detect c++ code types. ```lua includes("check_ctypes.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_ctypes("HAS_WCHAR", "wchar_t") configvar_check_ctypes("HAS_WCHAR_AND_FLOAT", {"wchar_t", "float"}) ``` ```c [config.h.in] ${define HAS_WCHAR} ${define HAS_WCHAR_AND_FLOAT} ``` ```c [config.h] /* #undef HAS_WCHAR */ /* #undef HAS_WCHAR_AND_FLOAT */ ``` ## Detect c/c++ functions `configvar_check_cfuncs` is used to detect c code functions, and `configvar_check_cxxfuncs` is used to detect c++ code functions. ```lua includes("check_cfuncs.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_cfuncs("HAS_SETJMP", "setjmp", {includes = {"signal.h", "setjmp.h"}}) ``` ```c [config.h.in] ${define HAS_SETJMP} ``` ```c [config.h] define HAS_SETJMP 1 /* #undef HAS_SETJMP */ ``` ## Detect c/c++ header files `configvar_check_cincludes` is used to detect c code header files, and `configvar_check_cxxincludes` is used to detect c++ code header files. ```lua includes("check_cincludes.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_cincludes("HAS_STRING_H", "string.h") configvar_check_cincludes("HAS_STRING_AND_STDIO_H", {"string.h", "stdio.h"}) ``` ```c [config.h.in] ${define HAS_STRING_H} ${define HAS_STRING_AND_STDIO_H} ``` ```c [config.h] /* #undef HAS_STRING_H */ define HAS_STRING_AND_STDIO_H 1 ``` ## Detect c/c++ code snippets `configvar_check_csnippets` is used to detect c code snippets, and `configvar_check_cxxsnippets` is used to detect c++ code snippets. ```lua includes("check_csnippets.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_csnippets("HAS_STATIC_ASSERT", "_Static_assert(1, \"\");") ``` ```c [config.h.in] ${define HAS_STATIC_ASSERT} ``` ```c [config.h] define HAS_STATIC_ASSERT 1 ``` After v2.5.7, check\_csnippets has been improved, adding `tryrun` and `output` parameters to try to run and capture output. ```lua includes("check_csnippets.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") check_csnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0: -1;", {tryrun = true}) check_csnippets("INT_SIZE",'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) configvar_check_csnippets("HAS_LONG_8", "return (sizeof(long) == 8)? 0: -1;", {tryrun = true}) configvar_check_csnippets("PTR_SIZE",'printf("%d", sizeof(void*)); return 0;', {output = true, number = true}) ``` If capture output is enabled, `${define PTR_SIZE}` in `config.h.in` will automatically generate `#define PTR_SIZE 4`. Among them, the `number = true` setting can be forced as a number instead of a string value, otherwise it will be defined as `#define PTR_SIZE "4"` by default ## Detecting compiler features ```lua includes("check_features.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_features("HAS_CONSTEXPR", "cxx_constexpr") configvar_check_features("HAS_CONSEXPR_AND_STATIC_ASSERT", {"cxx_constexpr", "c_static_assert"}, {languages = "c++11"}) ``` ```c [config.h.in] ${define HAS_CONSTEXPR} ${define HAS_CONSEXPR_AND_STATIC_ASSERT} ``` ```c [config.h] /* #undef HAS_CONSTEXPR */ define HAS_CONSEXPR_AND_STATIC_ASSERT 1 ``` List of all c compiler features: | Feature name | | --------------------- | | c\_static\_assert | | c\_restrict | | c\_variadic\_macros | | c\_function\_prototypes | 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 | v2.5.9 adds c++17 feature detection: | Feature name | | ------------------------------------ | | cxx\_aggregate\_bases | | cxx\_aligned\_new | | cxx\_capture\_star\_this | | cxx\_constexpr | | cxx\_deduction\_guides | | cxx\_enumerator\_attributes | | cxx\_fold\_expressions | | cxx\_guaranteed\_copy\_elision | | cxx\_hex\_float | | cxx\_if\_constexpr | | cxx\_inheriting\_constructors | | cxx\_inline\_variables | | cxx\_namespace\_attributes | | cxx\_noexcept\_function\_type | | cxx\_nontype\_template\_args | | cxx\_nontype\_template\_parameter\_auto | | cxx\_range\_based\_for | | cxx\_static\_assert | | cxx\_structured\_bindings | | cxx\_template\_template\_args | | cxx\_variadic\_using | v2.5.9 adds c++20 feature detection: | Feature name | | ------------------------------------ | | cxx\_aggregate\_paren\_init | | cxx\_char8\_t | | cxx\_concepts | | cxx\_conditional\_explicit | | cxx\_consteval | | cxx\_constexpr | | cxx\_constexpr\_dynamic\_alloc | | cxx\_constexpr\_in\_decltype | | cxx\_constinit | | cxx\_deduction\_guides | | cxx\_designated\_initializers | | cxx\_generic\_lambdas | | cxx\_impl\_coroutine | | cxx\_impl\_destroying\_delete | | cxx\_impl\_three\_way\_comparison | | cxx\_init\_captures | | cxx\_modules | | cxx\_nontype\_template\_args | | cxx\_using\_enum | After 2.5.8, support for cstd and c++ std versions are added, related issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) ```lua configvar_check_features("HAS_CXX_STD_98", "cxx_std_98") configvar_check_features("HAS_CXX_STD_11", "cxx_std_11", {languages = "c++11"}) configvar_check_features("HAS_CXX_STD_14", "cxx_std_14", {languages = "c++14"}) configvar_check_features("HAS_CXX_STD_17", "cxx_std_17", {languages = "c++17"}) configvar_check_features("HAS_CXX_STD_20", "cxx_std_20", {languages = "c++20"}) configvar_check_features("HAS_C_STD_89", "c_std_89") configvar_check_features("HAS_C_STD_99", "c_std_99") configvar_check_features("HAS_C_STD_11", "c_std_11", {languages = "c11"}) configvar_check_features("HAS_C_STD_17", "c_std_17", {languages = "c17"}) ``` ## Detect built-in macro definitions There are some built-in macro definitions in the compiler, such as `__GNUC__`, etc. We can use the `check_macros` and `configvar_check_macros` auxiliary scripts to detect their existence. Related issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) ```lua - Check whether the macro is defined configvar_check_macros("HAS_GCC", "__GNUC__") - The detection macro is not defined configvar_check_macros("NO_GCC", "__GNUC__", {defined = false}) - Detect macro conditions configvar_check_macros("HAS_CXX20", "__cplusplus >= 202002L", {languages = "c++20"}) ``` ## Detect type size In previous versions, type detection was possible with `check_csnippets` and `output = true`. ```lua check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` This way, however, extracts the type size information by trying to run the test code and then getting the run output. This doesn't work for cross-compilation. In version 2.8.5, we added the `check_sizeof` helper interface, which allows you to extract type size information by parsing the binary file of the test program directly. Since there is no need to run tests, this approach not only supports cross-compilation, but also greatly improves the efficiency of testing and is much easier to use. ```lua includes("@builtin/check") target("test") set_kind("static") add_files("*.cpp") check_sizeof("LONG_SIZE", "long") check_sizeof("STRING_SIZE", "std::string", {includes = "string"}) ``` ```sh $ xmake f -c checking for LONG_SIZE ... 8 checking for STRING_SIZE ... 24 ``` Alternatively, I can check for it in the script field with `target:check_sizeof`. ## Detecting big-endian After version 2.8.9, we added the `check_bigendian` interface to determine if the current compilation target is in bigendian mode. ```lua includes("@builtin/check") target("test") set_kind("static") add_files("*.cpp") check_bigendian("IS_BIG_ENDIAN") ``` If the test passes and it is currently in big endian mode, then `IS_BIG_ENDIAN=1` is defined. --- --- url: /posts/how-to-build-a-simple-project.md --- We create an empty console project first: ```bash $ xmake create -P ./hello create hello ... create ok!👌 ``` And xmake will generate some files: ```bash $ cd ./hello $ tree . . ├── src │   └── main.c └── xmake.lua ``` It is a simple console program only for printing `hello xmake!` ```bash $ cat ./src/main.c #include int main(int argc, char** argv) { printf("hello xmake!\n"); return 0; } ``` The content of `xmake.lua` is very simple: ```lua $ cat xmake.lua target("hello") set_kind("binary") add_files("src/*.c") ``` We build it now. ```bash $ xmake checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.11 checking for the target minimal version ... 10.11 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the debugger (dd) ... xcrun -sdk macosx lldb configure { ex = "xcrun -sdk macosx ar" , ccache = "ccache" , plat = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , sh = "xcrun -sdk macosx clang++" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , xcode_dir = "/Applications/Xcode.app" , target_minver = "10.11" , sc = "xcrun -sdk macosx swiftc" , mode = "release" , make = "make" , cc = "xcrun -sdk macosx clang" , host = "macosx" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.11" , cxx = "xcrun -sdk macosx clang" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: ccache compiling.release src/main.c [100%]: linking.release hello build ok!👌 ``` And we run this program: ```bash $ xmake run hello hello world! ``` Or we can debug it. ```bash $ xmake run -d hello [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]$ ``` Next we build it for android. ```bash $ xmake f -p android --ndk=~/files/android-ndk-r10e/ checking for the architecture ... armv7-a checking for the SDK version of NDK ... android-21 checking for the c compiler (cc) ... arm-linux-androideabi-gcc checking for the c++ compiler (cxx) ... arm-linux-androideabi-g++ checking for the assember (as) ... arm-linux-androideabi-gcc checking for the linker (ld) ... arm-linux-androideabi-g++ checking for the static library archiver (ar) ... arm-linux-androideabi-ar checking for the static library extractor (ex) ... arm-linux-androideabi-ar checking for the shared library linker (sh) ... arm-linux-androideabi-g++ configure { ex = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar" , ccache = "ccache" , ndk = "~/files/android-ndk-r10e/" , sc = "xcrun -sdk macosx swiftc" , ar = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar" , ld = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++" , buildir = "build" , host = "macosx" , as = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc" , toolchains = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin" , arch = "armv7-a" , mxx = "xcrun -sdk macosx clang++" , xcode_dir = "/Applications/Xcode.app" , target_minver = "10.11" , ndk_sdkver = 21 , mode = "release" , cc = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc" , cxx = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++" , make = "make" , dd = "xcrun -sdk macosx lldb" , kind = "static" , sh = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++" , xcode_sdkver = "10.11" , plat = "android" , mm = "xcrun -sdk macosx clang" } configure ok! $ xmake clean ok! [00%]: ccache compiling.release src/main.c [100%]: linking.release hello build ok!👌 ``` Or we build it for iphoneos. ```bash $ xmake f -p iphoneos checking for the architecture ... armv7 checking for the Xcode SDK version for iphoneos ... 9.2 checking for the target minimal version ... 9.2 checking for the c compiler (cc) ... xcrun -sdk iphoneos clang checking for the c++ compiler (cxx) ... xcrun -sdk iphoneos clang checking for the objc compiler (mm) ... xcrun -sdk iphoneos clang checking for the objc++ compiler (mxx) ... xcrun -sdk iphoneos clang++ checking for the assember (as) ... gas-preprocessor.pl xcrun -sdk iphoneos clang checking for the linker (ld) ... xcrun -sdk iphoneos clang++ checking for the static library archiver (ar) ... xcrun -sdk iphoneos ar checking for the static library extractor (ex) ... xcrun -sdk iphoneos ar checking for the shared library linker (sh) ... xcrun -sdk iphoneos clang++ checking for the swift compiler (sc) ... xcrun -sdk iphoneos swiftc configure { ex = "xcrun -sdk iphoneos ar" , ccache = "ccache" , ndk = "~/files/android-ndk-r10e/" , sc = "xcrun -sdk iphoneos swiftc" , ar = "xcrun -sdk iphoneos ar" , sh = "xcrun -sdk iphoneos clang++" , buildir = "build" , xcode_dir = "/Applications/Xcode.app" , as = "/usr/local/share/xmake/tools/utils/gas-preprocessor.pl xcrun -sdk iphoneos clang" , toolchains = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin" , arch = "armv7" , mxx = "xcrun -sdk iphoneos clang++" , ndk_sdkver = 21 , target_minver = "9.2" , cc = "xcrun -sdk iphoneos clang" , mode = "release" , host = "macosx" , cxx = "xcrun -sdk iphoneos clang" , make = "make" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk iphoneos clang++" , xcode_sdkver = "9.2" , plat = "iphoneos" , mm = "xcrun -sdk iphoneos clang" } configure ok! $ xmake [00%]: ccache compiling.release src/main.c [100%]: linking.release hello build ok!👌 ``` Last, we attempt to build it for mingw. ```bash $ xmake f -p mingw --sdk=/usr/local/i386-mingw32-4.3.0/ checking for the architecture ... i386 checking for the c compiler (cc) ... i386-mingw32-gcc checking for the c++ compiler (cxx) ... i386-mingw32-g++ checking for the assember (as) ... i386-mingw32-gcc checking for the linker (ld) ... i386-mingw32-g++ checking for the static library archiver (ar) ... i386-mingw32-ar checking for the static library extractor (ex) ... i386-mingw32-ar checking for the shared library linker (sh) ... i386-mingw32-g++ checking for the swift compiler (sc) ... no configure { ex = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-ar" , ccache = "ccache" , ndk = "~/files/android-ndk-r10e/" , sc = "xcrun -sdk iphoneos swiftc" , sdk = "/usr/local/i386-mingw32-4.3.0/" , cc = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-gcc" , ndk_sdkver = 21 , buildir = "build" , plat = "mingw" , as = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-gcc" , toolchains = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin" , arch = "i386" , mxx = "xcrun -sdk iphoneos clang++" , xcode_dir = "/Applications/Xcode.app" , target_minver = "9.2" , sh = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-g++" , mode = "release" , host = "macosx" , cxx = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-g++" , make = "make" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ar = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-ar" , xcode_sdkver = "9.2" , ld = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-g++" , mm = "xcrun -sdk iphoneos clang" } configure ok! $ xmake [00%]: ccache compiling.release src/main.c [100%]: linking.release hello.exe build ok!👌 ``` xmake can build project directly on cmd if your system is windows and it will check your vs tools automatically. .e.g ```bash $ xmake checking for the architecture ... x86 checking for the Microsoft Visual Studio version ... 2008 checking for the c compiler (cc) ... cl.exe checking for the c++ compiler (cxx) ... cl.exe checking for the assember (as) ... ml.exe checking for the linker (ld) ... link.exe checking for the static library archiver (ar) ... link.exe -lib checking for the shared library linker (sh) ... link.exe -dll checking for the static library extractor (ex) ... lib.exe configure { ex = "lib.exe" , sh = "link.exe -dll" , host = "windows" , ar = "link.exe -lib" , as = "ml.exe" , plat = "windows" , buildir = "build" , arch = "x86" , cc = "cl.exe" , cxx = "cl.exe" , mode = "release" , clean = true , kind = "static" , ld = "link.exe" , vs = "2008" } configure ok! [00%]: compiling.release src\main.c [100%]: linking.release hello.exe build ok! ``` --- --- url: /posts/how-to-compile-on-cross-toolchains.md --- 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 ``` However, we need set it manually if the toolchains `/bin` directory is in other places. ```bash $ xmake f -p linux --sdk=/home/toolchains_sdkdir --toolchains=/usr/opt/bin $ xmake ``` Or we need configure all options for compiling successfully if this toolchains has completely differen directory structure. ```bash $ xmake f -p linux --sdk=/home/toolchains_sdkdir --toolchains=/usr/opt/bin --cxflags="-I/usr/xxx/include" --ldflags="-L/usr/zzz/lib" $ xmake ``` We can also set the prefix using the argument `--cross=` manually. .e.g ```bash $ xmake f -p linux --cross=arm-linux- --sdk=/home/toolchains_sdkdir ... ``` --- --- url: /posts/how-to-install-xmake.md --- ### Install on windows 1. Download xmake source codes 2. Enter the source code directory 3. Run `install.bat` 4. Select the installed directory and enter into this directory 5. Please wait some mintues ### Install from source codes on linux and macosx ```bash $ git clone git@github.com:waruqi/xmake.git $ cd ./xmake $ sudo ./install ``` ### Install using homebrew on macosx ```bash $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" $ sudo brew install xmake ``` ### Install using homebrew on linux ```bash $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/linuxbrew/go/install)" $ sudo brew install xmake ``` --- --- url: /guide/extensions/ide-integration-plugins.md --- # IDE Integration Plugins ## VSCode Plugin * [xmake-vscode](https://github.com/xmake-io/xmake-vscode) [VSCode](https://code.visualstudio.com/) is a commonly used text editor, and xmake provides plugin support. ### Plugin installation Since VSCode itself only provides text editing functions, we need to install plugins to support configuration, compilation, debugging, intellisense, and other functions: * XMake * C/C++ * CodeLLDB After completing the installation of the plugin, restart VSCode to see the status bar below: ![](/assets/img/guide/vscode_status_bar.png) You can set the platform, architecture, compilation mode, tool-chain and other options in the status bar, and then click Build to start the build. ### Custom options If these options are not enough, you can create .vscode/settings.json and write the settings required by xmake, such as: ``` { ... "xmake.additionalConfigArguments": [ "--my_option=true" ], ... } ``` Other xmake options can also be set in settings.json. After modification, the configuration can be refreshed through the >XMake: Configure command. ### Configure Intellsence For a better C++ syntax prompt experience, xmake provides support for [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) (LSP for short). In VSCode, we can provide intellisense support by using vscode-cpptools or clangd. In addition, in order to support intellisense, xmake provides compile\_commands.json generation support. #### generate compile\_commands ##### Automatic trigger generation Usually after modifying xmake.lua and clicking Save, the xmake-vscode plugin will trigger the automatic generation of compile\_commands.json, which is stored in the .vscode directory by default. This is also the recommended way. Usually after installing the xmake-vscode plugin and opening the project with xmake.lua, you only need to edit xmake.lua to save and trigger without any other additional operations. ##### Manually trigger generation Of course, if we don't see the file being generated, we can also use the `>XMake: UpdateIntellisense` command to manually trigger the generation of .vscode/compile\_commands.json in VSCode. ##### Configure xmake.lua to generate automatically Alternatively, we can also use this rule to automatically update and generate compile\_commands.json ```lua add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"}) target("test") set_kind("binary") add_files("src/*.c") ``` This will automatically update this file after each build. ##### Manual execution command generation If the above methods are invalid, we can also execute the command to generate it. ```sh $ xmake project -k compile_commands .vscode ``` #### vscode-cpptools If we use the vscode-cpptools plugin to provide intellisense support, we need to go to the VSCode plugin marketplace first, search for C++, and install the default first plugin. When installed, this plugin provides intellisense and debugging support. Then, we need to configure the c\_cpp\_properties.json file and associate it with the `.vscode/compile_commands.json` we generated. ``` { "env": { "myDefaultIncludePath": ["${workspaceFolder}", "${workspaceFolder}/include"], "myCompilerPath": "/usr/local/bin/gcc-7" }, "configurations": [ { "name": "Mac", "intelliSenseMode": "clang-x64", "includePath": ["${myDefaultIncludePath}", "/another/path"], "macFrameworkPath": ["/System/Library/Frameworks"], "defines": ["FOO", "BAR=100"], "forcedInclude": ["${workspaceFolder}/include/config.h"], "compilerPath": "/usr/bin/clang", "cStandard": "c11", "cppStandard": "c++17", "compileCommands": "/path/to/compile_commands.json", "browse": { "path": ["${workspaceFolder}"], "limitSymbolsToIncludedHeaders": true, "databaseFilename": "" } } ], "version": 4 } ``` That is the `"compileCommands": "/path/to/compile_commands.json"` configuration item above. For how to open this configuration file, and more configuration instructions, see: * https://code.visualstudio.com/docs/cpp/configure-intellisense-crosscompilation * https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference Of course, in theory, the xmake-vscode plugin can automatically associate and set this file, but considering that users do not necessarily use cpptools, they may also use clangd. Therefore, the default automatic configuration is not very good, and the author has no time and energy to improve it. #### clangd When using clangd, there may be conflicts with the C/C++ plugin, you can add settings in .vscode/settings.json: ``` { "C_Cpp.codeAnalysis.runAutomatically": false, "C_Cpp.intelliSenseEngine": "Disabled", "C_Cpp.formatting": "Disabled", "C_Cpp.autoAddFileAssociations": false, "C_Cpp.autocompleteAddParentheses": false, "C_Cpp.autocomplete": "Disabled", "C_Cpp.errorSquiggles": "Disabled", ... } ``` Also, since the compile\_commands.json generated by XMake is in the .vscode directory, you need to set the clangd parameter to look for it in the correct location: ``` { "clangd.arguments": [ "--compile-commands-dir=.vscode", ... ] ... } ``` ## Sublime Plugin * [xmake-sublime](https://github.com/xmake-io/xmake-sublime) ## Intellij IDEA/Clion Plugin * [xmake-idea](https://github.com/xmake-io/xmake-idea) ## Vim Plugin * [xmake.vim](https://github.com/luzhlon/xmake.vim) (third-party, thanks [@luzhlon](https://github.com/luzhlon)) ## Neovim Plugin * [xmake.nvim](https://github.com/Mythos-404/xmake.nvim) (third-party, thanks [@Mythos\_404](https://github.com/Mythos-404)) The plugin provides an easy-to-use configuration UI and auto-generation of *compile\_commands.json* files ## Gradle Plugin (JNI) * [xmake-gradle](https://github.com/xmake-io/xmake-gradle): A gradle plugin that integrates xmake seamlessly ### plugins DSL ``` plugins { id 'org.tboox.gradle-xmake-plugin' version '1.2.3' } ``` ### Legacy plugin application ``` buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath 'org.tboox:gradle-xmake-plugin:1.2.3' } repositories { mavenCentral() } } apply plugin: "org.tboox.gradle-xmake-plugin" ``` ### Simplest Example We add `xmake.lua` to `projectdir/jni/xmake.lua` and enable xmake in build.gradle. #### build.gradle ``` android { externalNativeBuild { xmake { path "jni/xmake.lua" } } } ``` #### JNI The JNI project structure: ``` projectdir - src - main - java - jni - xmake.lua - *.cpp ``` xmake.lua: ```lua add_rules("mode.debug", "mode.release") target("nativelib") set_kind("shared") add_files("nativelib.cc") ``` ### More Gradle Configuations ``` android { defaultConfig { externalNativeBuild { xmake { // append the global cflags (optional) cFlags "-DTEST" // append the global cppflags (optional) cppFlags "-DTEST", "-DTEST2" // switch the build mode to `debug` for `xmake f -m debug` (optional) buildMode "debug" // set abi filters (optional), e.g. armeabi, armeabi-v7a, arm64-v8a, x86, x86_64 // we can also get abiFilters from defaultConfig.ndk.abiFilters abiFilters "armeabi-v7a", "arm64-v8a" } } } externalNativeBuild { xmake { // enable xmake and set xmake.lua project file path path "jni/xmake.lua" // enable verbose output (optional), e.g. verbose, warning, normal logLevel "verbose" // set c++stl (optional), e.g. c++_static/c++_shared, gnustl_static/gnustl_shared, stlport_static/stlport_shared stl "c++_shared" // set the given xmake program path (optional) // program /usr/local/bin/xmake // disable stdc++ library (optional) // stdcxx false // set the given ndk directory path (optional) // ndk "/Users/ruki/files/android-ndk-r20b/" // set sdk version of ndk (optional) // sdkver 21 } } } ``` ### Build JNI and generate apk The `xmakeBuild` will be injected to `assemble` task automatically if the gradle-xmake-plugin has been applied. ```sh $ ./gradlew app:assembleDebug > Task :nativelib:xmakeConfigureForArm64 > Task :nativelib:xmakeBuildForArm64 >> xmake build [ 50%]: cache compiling.debug nativelib.cc [ 75%]: linking.debug libnativelib.so [100%]: build ok! >> install artifacts to /Users/ruki/projects/personal/xmake-gradle/nativelib/libs/arm64-v8a > Task :nativelib:xmakeConfigureForArmv7 > Task :nativelib:xmakeBuildForArmv7 >> xmake build [ 50%]: cache compiling.debug nativelib.cc [ 75%]: linking.debug libnativelib.so [100%]: build ok! >> install artifacts to /Users/ruki/projects/personal/xmake-gradle/nativelib/libs/armeabi-v7a > Task :nativelib:preBuild > Task :nativelib:assemble > Task :app:assembleDebug ``` ### Force to rebuild JNI ```sh $ ./gradlew nativelib:xmakeRebuild ``` --- --- url: /zh/guide/extensions/ide-integration-plugins.md --- # IDE 集成插件 {#ide-integration-plugins} ## VSCode 插件 {#vscode-plugin} * [xmake-vscode](https://github.com/xmake-io/xmake-vscode) [VSCode](https://code.visualstudio.com/) 是常用的文本编辑器,xmake 提供了插件支持。 ### 插件安装 由于 VSCode 本身只提供了文本编辑功能,我们需要安装插件以支持配置、编译、调试、语法提示等功能: * XMake * C/C++ * CodeLLDB 在完成插件的安装后,重启 VSCode 可以看到下方的状态栏: ![](/assets/img/guide/vscode_status_bar.png) 可以在状态栏设置平台、架构、编译模式、工具链等选项,随后点击 Build 开始构建。 ### 自定义选项 如果这些选项不够,可以创建 .vscode/settings.json 并编写 xmake 需要的设置,如: ``` { ... "xmake.additionalConfigArguments": [ "--my_option=true" ], ... } ``` 其他 xmake 的选项也同样可以在 settings.json 中完成设置。修改后可通过 >XMake: Configure 命令刷新配置。 ### 配置 IntelliSense {#intellisense} 为了更好的 C++ 语法提示体验,xmake 提供了对 [Language Server Protocol](https://microsoft.github.io/language-server-protocol/)(简称 LSP)的支持。 在 VSCode 中,我们可以通过使用 vscode-cpptools 或 clangd 来提供 intellisense 支持。 另外,为了支持 intellisense,xmake 提供了 compile\_commands.json 的生成支持。 #### 生成 compile\_commands ##### 自动触发生成 通常在修改 xmake.lua 后点击保存,xmake-vscode 插件就会触发自动生成 compile\_commands.json,默认存储在 .vscode 目录下。 这也是推荐方式,通常装完 xmake-vscode 插件,打开带有 xmake.lua 的工程后,只需要编辑 xmake.lua 保存即可触发,不需要任何其他额外操作。 ##### 手动触发生成 当然,如果没看到文件被生成,我们也可以在 VSCode 中,使用 `>XMake: UpdateIntellisense` 命令手动触发生成 .vscode/compile\_commands.json。 ##### 配置 xmake.lua 自动生成 或者,我们也可以使用这个规则来自自动更新生成 compile\_commands.json ```lua add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"}) target("test") set_kind("binary") add_files("src/*.c") ``` 这会使得在每次 build 后,自动更新此文件。 ##### 手动执行命令生成 如果上述方式都无效,我们也可以执行命令来生成。 ```sh $ xmake project -k compile_commands .vscode ``` #### vscode-cpptools 如果我们使用 vscode-cpptools 插件来提供 intellisense 支持,需要先去 VSCode 插件市场,搜索 C++,默认第一个插件就是,安装即可。 安装后,这个插件提供了 intellisense 和调试支持。 然后,我们需要配置下 c\_cpp\_properties.json 文件,关联上我们生成的 `.vscode/compile_commands.json`。 ``` { "env": { "myDefaultIncludePath": ["${workspaceFolder}", "${workspaceFolder}/include"], "myCompilerPath": "/usr/local/bin/gcc-7" }, "configurations": [ { "name": "Mac", "intelliSenseMode": "clang-x64", "includePath": ["${myDefaultIncludePath}", "/another/path"], "macFrameworkPath": ["/System/Library/Frameworks"], "defines": ["FOO", "BAR=100"], "forcedInclude": ["${workspaceFolder}/include/config.h"], "compilerPath": "/usr/bin/clang", "cStandard": "c11", "cppStandard": "c++17", "compileCommands": "/path/to/compile_commands.json", "browse": { "path": ["${workspaceFolder}"], "limitSymbolsToIncludedHeaders": true, "databaseFilename": "" } } ], "version": 4 } ``` 也就是上面的 `"compileCommands": "/path/to/compile_commands.json"` 配置项。 关于如果打开这个配置文件,以及更多的配置说明,见: * https://code.visualstudio.com/docs/cpp/configure-intellisense-crosscompilation * https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference 当然,理论上可以做到 xmake-vscode 插件自动关联设置这个文件,但考虑到用户不一定使用 cpptools,也有可能会使用 clangd。 因此,默认自动配置并不是很好,而且作者暂时也没有时间和精力去改进它。 #### clangd 与此同时,我们可以选择安装支持 LSP 的语法提示插件,如 LLVM 推出的 [clangd](https://clangd.llvm.org/),其功能稳定且提示流畅, 并通过 LSP 标准完成对不同编译工具链的支持。 使用 clangd 时,可能与上述的 C/C++ 插件的提示功能有冲突,可以在 .vscode/settings.json 中添加设置将 C/C++ 的语法提示功能关闭: ``` { "C_Cpp.codeAnalysis.runAutomatically": false, "C_Cpp.intelliSenseEngine": "Disabled", "C_Cpp.formatting": "Disabled", "C_Cpp.autoAddFileAssociations": false, "C_Cpp.autocompleteAddParentheses": false, "C_Cpp.autocomplete": "Disabled", "C_Cpp.errorSquiggles": "Disabled", ... } ``` 同时由于 XMake 生成的 compile\_commands.json 在 .vscode 目录,还需要设置 clangd 参数使其在正确位置寻找: ``` { "clangd.arguments": [ "--compile-commands-dir=.vscode", ... ] ... } ``` 如果配置后,还是没生效,可以尝试重启 VSCode 和 clangd 进程,再验证。 ## Sublime 插件 {#sublime-plugin} * [xmake-sublime](https://github.com/xmake-io/xmake-sublime) ## Intellij IDEA/CLion 插件 {#clion-plugin} * [xmake-idea](https://github.com/xmake-io/xmake-idea) ## Vim 插件 {#vim-plugin} * [xmake.vim](https://github.com/luzhlon/xmake.vim) (第三方开发, 感谢[@luzhlon](https://github.com/luzhlon)) ## Neovim 插件 {#neovim-plugin} * [xmake.nvim](https://github.com/Mythos-404/xmake.nvim) (第三方开发, 感谢[@Mythos\_404](https://github.com/Mythos-404)) 该插件提供了易用的配置UI和自动生成*compile\_commands.json*文件 ## Gradle插件(JNI){#gradle-plugin} * [xmake-gradle](https://github.com/xmake-io/xmake-gradle): 一个无缝整合 xmake 的 gradle 插件 ### 通过插件 DSL 集成 ``` plugins { id 'org.tboox.gradle-xmake-plugin' version '1.2.3' } ``` ### 被废弃的插件集成方式 ``` buildscript { repositories { maven { url "https://plugins.gradle.org/m2/" } } dependencies { classpath 'org.tboox:gradle-xmake-plugin:1.2.3' } repositories { mavenCentral() } } apply plugin: "org.tboox.gradle-xmake-plugin" ``` ### 最简单的配置示例 如果我们添加`xmake.lua`文件到`projectdir/jni/xmake.lua`,那么我们只需要在build.gradle中启用生效了xmake指定下对应的JNI工程路径即可。 #### build.gradle ``` android { externalNativeBuild { xmake { path "jni/xmake.lua" } } } ``` #### JNI JNI工程结构 ``` projectdir - src - main - java - jni - xmake.lua - *.cpp ``` xmake.lua: ```lua add_rules("mode.debug", "mode.release") target("nativelib") set_kind("shared") add_files("nativelib.cc") ``` ### 更多Gradle配置说明 ``` android { defaultConfig { externalNativeBuild { xmake { // 追加设置全局c编译flags cFlags "-DTEST" // 追加设置全局c++编译flags cppFlags "-DTEST", "-DTEST2" // 设置切换编译模式,与`xmake f -m debug`的配置对应,具体模式值根据自己的xmake.lua设置而定 buildMode "debug" // 设置需要编译的abi列表,支持:armeabi, armeabi-v7a, arm64-v8a, x86, x86_64 // 如果没有设置的话,我们也支持从defaultConfig.ndk.abiFilters中获取abiFilters abiFilters "armeabi-v7a", "arm64-v8a" } } } externalNativeBuild { xmake { // 设置jni工程中xmake.lua根文件路径,这是必须的,不设置就不会启用jni编译 path "jni/xmake.lua" // 启用详细输出,会显示完整编译命令行参数,其他值:verbose, warning, normal logLevel "verbose" // 指定c++ stl库,默认不指定会使用c++_static,其他值:c++_static/c++_shared, gnustl_static/gnustl_shared, stlport_static/stlport_shared stl "c++_shared" // 设置xmake可执行程序路径(通常不用设置) // program /usr/local/bin/xmake // 禁用stdc++库,默认是启用的 // stdcxx false // 设置其他指定的ndk目录路径 (这是可选的,默认xmake会自动从$ANDROID_NDK_HOME或者`~/Library/Android/sdk/ndk-bundle`中检测) // 当然如果用户通过`xmake g --ndk=xxx`配置了全局设置,也会自动从这个里面检测 // ndk "/Users/ruki/files/android-ndk-r20b/" // 设置ndk中sdk版本 // sdkver 21 } } } ``` ### 编译JNI并且生成APK 当`gradle-xmake-plugin`插件被应用生效后,`xmakeBuild`任务会自动注入到现有的`assemble`任务中去,自动执行jni库编译和集成。 ```sh $ ./gradlew app:assembleDebug > Task :nativelib:xmakeConfigureForArm64 > Task :nativelib:xmakeBuildForArm64 >> xmake build [ 50%]: cache compiling.debug nativelib.cc [ 75%]: linking.debug libnativelib.so [100%]: build ok! >> install artifacts to /Users/ruki/projects/personal/xmake-gradle/nativelib/libs/arm64-v8a > Task :nativelib:xmakeConfigureForArmv7 > Task :nativelib:xmakeBuildForArmv7 >> xmake build [ 50%]: cache compiling.debug nativelib.cc [ 75%]: linking.debug libnativelib.so [100%]: build ok! >> install artifacts to /Users/ruki/projects/personal/xmake-gradle/nativelib/libs/armeabi-v7a > Task :nativelib:preBuild > Task :nativelib:assemble > Task :app:assembleDebug ``` ### 强制重建JNI ```sh $ ./gradlew nativelib:xmakeRebuild ``` --- --- url: /api/scripts/builtin-modules/import.md --- # import * Import extension modules #### Function Prototype ::: tip API ```lua import(modulename: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | modulename | Module name string | | options | Import options table (optional) | #### Usage 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) ``` ::: tip NOTE In order to ensure that the description scope 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 scope, as shown in the following table: An example of using an interface call in a description scope 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 scope and description scope mentioned above mainly refer to: ```lua -- description scope target("test") -- description scope set_kind("static") add_files("src/*.c") on_run(function (target) -- Script domain end) -- description scope ``` ## 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](/api/description/project-target#on-build), [on\_run](/api/description/project-target#on-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.base.task") function main() -- Get parameter options print(option.get("version")) -- Run tasks and plugins 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. utils.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. ## Custom extension module Through import, we can import not only many built-in extension modules of xmake, but also user-defined extension modules. Just put your own module in the project directory and import it according to the import method described above. So, what if you want to define a module? xmake has a set of convention rules for module writing specifications, and does not follow Lua's native require import mechanism, and there is no need to use return in the module to return it globally. If we have a module file foo.lua, its content is as follows: ```lua function _foo(a, b) return a + b end function add(a, b) _foo(a, b) end function main(a, b) add(a, b) end ``` Among them main is the entry function, optional, if set, the module foo can be called directly, for example: ```lua import("foo") foo(1, 2) ``` Or directly like this: ```lua import("foo")(1, 2) ``` Others without underscore are public module interface functions, such as add. ```lua import("foo") foo.add(1, 2) ``` The underscore prefixed `_foo` is a private function that is used internally by the module and is not exported, so users cannot call it outside. --- --- url: /zh/api/scripts/builtin-modules/import.md --- # import * 导入扩展模块 #### 函数原型 ::: tip API ```lua import(modulename: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | modulename | 模块名称字符串 | | options | 导入选项表(可选) | #### 用法说明 在自定义脚本、插件脚本、任务脚本、平台扩展、模板扩展等脚本代码中使用,也就是在类似下面的代码块中,可以使用这些模块接口: ```lua on_run(function (target) print("hello xmake!") end) ``` ::: tip 注意 为了保证外层的描述域尽可能简洁、安全,一般不建议在这个域使用接口和模块操作api,因此大部分模块接口只能脚本域使用,来实现复杂功能 ::: 当然少部分只读的内置接口还是可以在描述域使用的,具体见下表: 在描述域使用接口调用的实例如下,一般仅用于条件控制: ```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) -- 描述域 ``` ## 导入扩展摸块 import 的主要用于导入xmake的扩展类库以及一些自定义的类库模块,一般用于: * 自定义脚本 ([on\_build](/zh/api/description/project-target#on-build), [on\_run](/zh/api/description/project-target#on-run) ..) * 插件开发 * 模板开发 * 平台扩展 * 自定义任务task 导入机制如下: 1. 优先从当前脚本目录下导入 2. 再从扩展类库中导入 导入的语法规则: 基于`.`的类库路径规则,例如: ```lua import("core.base.option") import("core.base.task") function main() -- 获取参数选项 print(option.get("version")) -- 运行任务和插件 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支持的平台列表了 utils.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接口返回导入的对象引用。 ## 自定义扩展模块 通过 import 我们除了可以导入 xmake 内置的很多扩展模块,还可以导入用户自己定义的扩展模块。 只需要将自己的模块放到工程目录下,按照上文介绍的导入方式进行导入即可。 那么,如果去定义模块呢?xmake 对模块的编写规范是有一套约定规则的,并没有沿用 lua 原生的 require 导入机制,并不需要在模块中使用 return 来全局返回它。 假如我们有一个模块文件 foo.lua,它的内容如下: ```lua function _foo(a, b) return a + b end function add(a, b) _foo(a, b) end function main(a, b) add(a, b) end ``` 其中 main 为入口函数,可选,如果设置,模块 foo 可以直接被调用,例如: ```lua import("foo") foo(1, 2) ``` 或者直接这样: ```lua import("foo")(1, 2) ``` 其他不带下划线的为 public 模块接口函数,例如 add。 ```lua import("foo") foo.add(1, 2) ``` 而里面带下划线前缀的 `_foo` 是私有函数,模块内部使用,不对外导出,所以在外面用户是不能够调用它的。 --- --- url: /api/scripts/builtin-modules/inherit.md --- # inherit * Import and inherit base class modules #### Function Prototype ::: tip API ```lua inherit(modulename: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | modulename | Module name string | #### Usage This is equivalent to the `inherit` mode of the [import](/api/scripts/builtin-modules/import) interface, which is: ```lua import("xxx.xxx", {inherit = true}) ``` With the `inherit` interface, it will be more concise: ```lua 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. --- --- url: /zh/api/scripts/builtin-modules/inherit.md --- # inherit * 导入并继承基类模块 #### 函数原型 ::: tip API ```lua inherit(modulename: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | modulename | 模块名称字符串 | #### 用法说明 这个等价于[import](/zh/api/scripts/builtin-modules/import)接口的 `inherit` 模式,也就是: ```lua import("xxx.xxx", {inherit = true}) ``` 用`inherit`接口的话,会更简洁些: ```lua inherit("xxx.xxx") ``` 使用实例,可以参看xmake的tools目录下的脚本:[clang.lua](https://github.com/xmake-io/xmake/blob/master/xmake/tools/clang.lua) 这个就是clang工具模块继承了gcc的部分实现。 --- --- url: /guide/basic-commands/install-and-uninstall.md --- # Install and Uninstall {#install-and-uninstall} When the compilation is complete, we can also use the `xmake install` command to install the target program and libraries to the system environment. This is usually used for installation in Linux, Unix, BSD, and other system environments. ::: tip NOTE Although it is also supported under Windows, it is not commonly used because there is no default installation directory on Windows. It is more recommended to generate the installation package directly. For details, see: [XPack packaging](/api/description/xpack-interfaces) ::: ## Command format ::: code-group ```sh [install] $ xmake install [options] [target] ``` ```sh [uninstall] $ xmake uninstall [options] [target] ``` ::: ## Install to the system directory Usually, we only need to execute the following command to complete the installation. The default installation directory is `/usr/local`. ```sh $ xmake install ``` ## Install to a specified directory We can also specify the root directory for installation, which is more useful when there is no default installation directory on Windows. ```sh $ xmake install -o /tmp/usr installing foo .. installing foo to /tmp/usr .. installing test .. installing test to /tmp/usr .. install ok! ruki:test ruki$ tree /tmp/usr /tmp/usr ├── bin │   └── test └── lib └── libfoo.a 2 directories, 2 files ``` In addition, we can also configure the default installation path in the configuration file through the [set\_installdir](/api/description/project-target#set-installdir) interface. For details, please refer to the API manual. ## Uninstaller We can also perform the reverse uninstallation operation through `xmake uninstall`, and specify the installation directory to be uninstalled. ```sh $ xmake uninstall $ xmake uninstall --installdir=/tmp/usr ``` --- --- url: /guide/introduction.md --- # Introduction ## What is Xmake? Xmake is a lightweight, cross-platform build utility based on Lua. It uses a Lua script to maintain project builds, but is driven by a dependency-free core program written in C. Compared with Makefiles or CMake, the configuration syntax is much more concise and intuitive. As such, it's friendly to novices while still maintaining the flexibility required in a build system. With Xmake, you can focus on your project instead of the build system. Xmake can be used to directly build source code (like with Make or Ninja), or it can generate project files like CMake or Meson. It also has a *built-in* package management system to help users integrate C/C++ dependencies. If you want to know more, please refer to the [Documentation](/guide/quick-start.html), [GitHub](https://github.com/xmake-io/xmake), or [Gitee](https://gitee.com/tboox/xmake). You are also welcome to join our [community](/about/contact.html). ![](/assets/img/index/xmake-basic-render.gif) --- --- url: /posts/project-description.md --- xmake's project description file abandons the tedious complexity of makefiles, learns from premake's simplicity and clarity, and natively supports lua scripts, making it more flexible and convenient to extend. The default project description file name is `xmake.lua`, which supports multi-level directory nesting. You can also specify other files as project description files through the following commands: ```bash xmake -f /tmp/xxx.lua xmake --file=xxx.lua ``` Let's first look at the simplest example: ```lua -- Add a target named demo to the project target("demo") -- Set target program type to binary executable, generally a console terminal command-line program set_kind("binary") -- Add all c files in src directory add_files("src/*.c") ``` How simple is that? This has already completed the simplest project description. Let's look at a slightly more complex example. In this example, different settings are made for release and debug modes: ```lua -- If currently compiling in debug mode if is_mode("debug") then -- Enable debug symbols set_symbols("debug") -- Disable optimization set_optimize("none") end -- If currently compiling in release mode if is_mode("release") then -- Set symbol visibility to hidden set_symbols("hidden") -- Enable fastest optimization mode set_optimize("fastest") -- Strip all symbol information, including debug symbols set_strip("all") end -- Add a target named test target("test") -- Compile test as static library type set_kind("static") -- Add all c++ files, including subdirectories (Note: ** indicates multi-level recursive matching pattern) add_files("src/**.cpp") ``` Actually, it's not very complicated. Since lua syntax is used, the logic is more flexible. You can completely use lua's branching, loops, functions and other syntax for more flexible configuration. --- --- url: /api/scripts/builtin-modules/io.md --- # io The io operation module extends lua's built-in io module to provide more easy-to-use interfaces. ## io.open * Open file for reading and writing #### Function Prototype ::: tip API ```lua io.open(filename: , mode: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | mode | Open mode string | | options | Options table (optional) | #### Usage This is a native Lua interface, extended by xmake. For detailed usage, see Lua's official documentation: [The Complete I/O Model](https://www.lua.org/pil/21.2.html) Supported open modes: `"r"` (read-only), `"w"` (write), `"a"` (append), `"r+"` (read-write), etc. It also supports specifying encoding format, for example: ```lua -- Open file with UTF-8 encoding local file = io.open("xxx.txt", "r", {encoding = "utf8"}) -- Open file with UTF-16LE encoding local file = io.open("xxx.txt", "r", {encoding = "utf16le"}) -- Open in binary mode local file = io.open("xxx.txt", "r", {encoding = "binary"}) ``` Supported encoding formats: `"utf8"` (default), `"utf16"`, `"utf16le"`, `"utf16be"`, `"binary"` If you want to read all the contents of the file, you can write: ```lua local file = io.open("$(tmpdir)/file.txt", "r") if file then local data = file:read("*all") file:close() end ``` Or you can read it more quickly using [io.readfile](#io-readfile). File objects also support the following extended methods: ```lua local file = io.open("xxx.txt", "r") -- Get file size local size = file:size() -- Get absolute file path local path = file:path() -- Read a line (preserving newline character) local line = file:read("L") -- Iterate line by line for line in file:lines() do print(line) end file:close() ``` If you want to write a file, you can do this: ```lua -- Open file: w is write mode, a is append write mode local file = io.open("xxx.txt", "w") if file then -- Write data to file with native lua interface, does not support formatting, no line breaks, does not support built-in variables file:write("hello xmake\n") -- Write data to file with xmake extended interface, support formatting, no line breaks, no built-in variables file:writef("hello %s\n", "xmake") -- Use xmake extended formatted parameters to write to one line, with line breaks, and support for built-in variables file:print("hello %s and $(buildir)", "xmake") -- Write a line using the xmake extended formatted arguments, no line breaks, and support for built-in variables file:printf("hello %s and $(buildir) \n", "xmake") -- Close the file file:close() end ``` ## io.load * Load all table contents from the specified path file deserialization #### Function Prototype ::: tip API ```lua io.load(filename: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | #### Usage You can load serialized table contents from a file, generally used with [io.save](#iosave), for example: ```lua -- Load the contents of the serialized file to the table local data = io.load("xxx.txt") if data then -- Dump prints the contents of the entire table in the terminal, formatting the output utils.dump(data) end ``` ## io.save * Serialize all table contents to the specified path file #### Function Prototype ::: tip API ```lua io.save(filename: , data:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | data | Table data to serialize | #### Usage You can serialize the contents of the table to the specified file, generally used in conjunction with [io.load](#ioload), for example: ```lua io.save("xxx.txt", {a = "a", b = "b", c = "c"}) ``` The result of the storage is: ``` { ["b"] = "b" , ["a"] = "a" , ["c"] = "c" } ``` ## io.readfile * Read everything from the specified path file #### Function Prototype ::: tip API ```lua io.readfile(filename: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | options | Options table (optional) | #### Usage It is more convenient to directly read the contents of the entire file without opening the file, for example: ```lua local data = io.readfile("xxx.txt") ``` Supports option parameters to control read behavior: ```lua -- Read in binary mode (no newline conversion) local data = io.readfile("xxx.txt", {encoding = "binary"}) -- Read UTF-16LE encoded file local data = io.readfile("xxx.txt", {encoding = "utf16le"}) -- Handle line continuation (lines ending with \ will be merged with the next line) local data = io.readfile("xxx.txt", {continuation = "\\"}) ``` Option parameters: * `encoding`: File encoding format, supports `"utf8"` (default), `"utf16"`, `"utf16le"`, `"utf16be"`, `"binary"` * `continuation`: Line continuation character, when specified, lines ending with this character will be merged with the next line (removing newlines) xmake automatically detects and handles different newline formats (LF, CRLF) and automatically detects UTF-8 BOM. ## io.writefile * Write all content to the specified path file #### Function Prototype ::: tip API ```lua io.writefile(filename: , data: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | data | Data string to write | #### Usage It is more convenient to directly write the contents of the entire file without opening the file, for example: ```lua io.writefile("xxx.txt", "all data") ``` ## io.gsub * Full text replaces the contents of the specified path file #### Function Prototype ::: tip API ```lua io.gsub(filename: , pattern: , replacement: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | pattern | Pattern string | | replacement | Replacement string | #### Usage Similar to the [string.gsub](#stringgsub) interface, the full-text pattern matches the replacement content, but here is the direct operation file, for example: ```lua -- Remove all whitespace characters from the file io.gsub("xxx.txt", "%s+", "") ``` ## io.tail * Read and display the tail content of the file #### Function Prototype ::: tip API ```lua io.tail(filename: , lines: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | lines | Number of lines to read | #### Usage Reads the data of the specified number of lines at the end of the file and displays a command like `cat xxx.txt | tail -n 10`, for example: ```lua -- Display the last 10 lines of the file io.tail("xxx.txt", 10) ``` ## io.cat * read and display all contents of the file #### Function Prototype ::: tip API ```lua io.cat(filename: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | #### Usage Read all the contents of the file and display it, similar to the `cat xxx.txt` command, for example: ```lua io.cat("xxx.txt") ``` ## io.print * Formatted output content to file with newline #### Function Prototype ::: tip API ```lua io.print(filename: , formatstring: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | formatstring | Format string | | ... | Variable arguments for formatting | #### Usage Directly format the passed parameter to output a line of string to the file with a line break, for example: ```lua io.print("xxx.txt", "hello %s!", "xmake") ``` ## io.printf * Formatted output to file without line breaks #### Function Prototype ::: tip API ```lua io.printf(filename: , formatstring: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | formatstring | Format string | | ... | Variable arguments for formatting | #### Usage Directly format the passed parameter to output a line of string to the file without a line break, for example: ```lua io.printf("xxx.txt", "hello %s!\n", "xmake") ``` ## io.lines * Read all lines from file #### Function Prototype ::: tip API ```lua io.lines(filename: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | options | Options table (optional) | #### Usage Returns an iterator function for all lines from a given file name. ```lua for line in io.lines("xxx.txt") do print(line) end ``` Can also be converted to an array: ```lua local lines = table.to_array(io.lines("xxx.txt")) ``` Supports the same option parameters as [io.readfile](#io-readfile): ```lua -- Read each line in binary mode (preserving CRLF) for line in io.lines("xxx.txt", {encoding = "binary"}) do print(line) end -- Handle line continuation for line in io.lines("xxx.txt", {continuation = "\\"}) do print(line) -- Lines ending with \ will be merged with the next line end ``` By default, newline characters are removed from each line. If you need to preserve newlines, use `file:read("L")` method. ## io.stdfile * Get a std file #### Function Prototype ::: tip API ```lua io.stdfile(stdname: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | stdname | Standard file name string | #### Usage Returns a file for a given std file name ```lua -- returns stdin io.stdin -- returns stdout io.stdout -- returns stderr io.stderr ``` ## io.openlock * Open a lock of a file #### Function Prototype ::: tip API ```lua io.openlock(filename: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | #### Usage Returns a file lock object when successfully locking the file ```lua local lock = io.openlock("xxx.txt") lock:lock() lock:unlock() lock:close() ``` ## io.replace * Replace text of the given file and return the replaced data #### Function Prototype ::: tip API ```lua io.replace(filename: , pattern: , replacement: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | File path string | | pattern | Pattern string | | replacement | Replacement string | | options | Options table (optional) | #### Usage Replaces a given pattern in a file by a replacement string ```lua -- replace string "Hello" in "xxx.txt" with "World" io.replace("xxx.txt", "Hello", "World") -- if you want to replace a string and not a pattern io.replace("xxx.txt", "1+1=2", "2+2=4", {plain = true}) ``` Option parameters: * `plain`: If true, use simple string matching; if false, use pattern matching * `encoding`: Specify file encoding format --- --- url: /zh/api/scripts/builtin-modules/io.md --- # io io 操作模块,扩展了 lua 内置的 io 模块,提供更多易用的接口。 ## io.open * 打开文件用于读写 #### 函数原型 ::: tip API ```lua io.open(filename: , mode: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filename | 文件路径字符串 | | mode | 打开模式字符串 | | options | 选项表(可选) | #### 用法说明 这个是属于lua的原生接口,xmake 在此基础上进行了扩展。详细使用可以参看lua的官方文档:[The Complete I/O Model](https://www.lua.org/pil/21.2.html) 支持的打开模式:`"r"`(只读)、`"w"`(写入)、`"a"`(追加)、`"r+"`(读写)等。 还支持指定编码格式,例如: ```lua -- 以 UTF-8 编码打开文件 local file = io.open("xxx.txt", "r", {encoding = "utf8"}) -- 以 UTF-16LE 编码打开文件 local file = io.open("xxx.txt", "r", {encoding = "utf16le"}) -- 以二进制模式打开 local file = io.open("xxx.txt", "r", {encoding = "binary"}) ``` 支持的编码格式:`"utf8"`(默认)、`"utf16"`、`"utf16le"`、`"utf16be"`、`"binary"` 如果要读取文件所有内容,可以这么写: ```lua local file = io.open("$(tmpdir)/file.txt", "r") if file then local data = file:read("*all") file:close() end ``` 或者可以使用[io.readfile](#io-readfile)更加快速地读取。 文件对象还支持以下扩展方法: ```lua local file = io.open("xxx.txt", "r") -- 获取文件大小 local size = file:size() -- 获取文件绝对路径 local path = file:path() -- 读取一行(保留换行符) local line = file:read("L") -- 逐行迭代 for line in file:lines() do print(line) end file:close() ``` 如果要写文件,可以这么操作: ```lua -- 打开文件:w 为写模式, a 为追加写模式 local file = io.open("xxx.txt", "w") if file then -- 用原生的lua接口写入数据到文件,不支持格式化,无换行,不支持内置变量 file:write("hello xmake\n") -- 用xmake扩展的接口写入数据到文件,支持格式化,无换行,不支持内置变量 file:writef("hello %s\n", "xmake") -- 使用xmake扩展的格式化传参写入一行,带换行符,并且支持内置变量 file:print("hello %s and $(buildir)", "xmake") -- 使用xmake扩展的格式化传参写入一行,无换行符,并且支持内置变量 file:printf("hello %s and $(buildir) \n", "xmake") -- 关闭文件 file:close() end ``` ## io.load * 从指定路径文件反序列化加载所有table内容 #### 函数原型 ::: tip API ```lua io.load(filename: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filename | 文件路径字符串 | #### 用法说明 可以从文件中加载序列化好的table内容,一般与[io.save](#iosave)配合使用,例如: ```lua -- 加载序列化文件的内容到table local data = io.load("xxx.txt") if data then -- 在终端中dump打印整个table中内容,格式化输出 utils.dump(data) end ``` ## io.save * 序列化保存所有table内容到指定路径文件 #### 函数原型 ::: tip API ```lua io.save(filename: , data:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filename | 文件路径字符串 | | data | 要序列化的表数据 | #### 用法说明 可以序列化存储table内容到指定文件,一般与[io.load](#ioload)配合使用,例如: ```lua io.save("xxx.txt", {a = "a", b = "b", c = "c"}) ``` 存储结果为: ``` { ["b"] = "b" , ["a"] = "a" , ["c"] = "c" } ``` ## io.readfile * 从指定路径文件读取所有内容 #### 函数原型 ::: tip API ```lua io.readfile(filename: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filename | 文件路径字符串 | | options | 选项表(可选) | #### 用法说明 可在不打开文件的情况下,直接读取整个文件的内容,更加的方便,例如: ```lua local data = io.readfile("xxx.txt") ``` 支持选项参数来控制读取行为: ```lua -- 以二进制模式读取(不进行换行符转换) local data = io.readfile("xxx.txt", {encoding = "binary"}) -- 读取 UTF-16LE 编码的文件 local data = io.readfile("xxx.txt", {encoding = "utf16le"}) -- 处理行继续符(如 \ 结尾的行会与下一行合并) local data = io.readfile("xxx.txt", {continuation = "\\"}) ``` 选项参数说明: * `encoding`:文件编码格式,支持 `"utf8"`(默认)、`"utf16"`、`"utf16le"`、`"utf16be"`、`"binary"` * `continuation`:行继续字符,指定后会将以该字符结尾的行与下一行合并(去除换行符) xmake 会自动检测并处理不同的换行符格式(LF、CRLF),并自动检测 UTF-8 BOM。 ## io.writefile * 写入所有内容到指定路径文件 #### 函数原型 ::: tip API ```lua io.writefile(filename: , content: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filename | 文件路径字符串 | | content | 文件内容字符串 | | options | 选项表(可选) | #### 用法说明 可在不打开文件的情况下,直接写入整个文件的内容,更加的方便,例如: ```lua io.writefile("xxx.txt", "all data") ``` ## io.gsub * 全文替换指定路径文件的内容 #### 函数原型 ::: tip API ```lua io.gsub(filepath: , pattern: , replace: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 文件路径字符串 | | pattern | 用于匹配的模式字符串 | | replace | 替换字符串或函数 | | options | 选项表(可选) | #### 用法说明 类似[string.gsub](#stringgsub)接口,全文模式匹配替换内容,不过这里是直接操作文件,例如: ```lua -- 移除文件所有的空白字符 io.gsub("xxx.txt", "%s+", "") ``` ## io.tail * 读取和显示文件的尾部内容 #### 函数原型 ::: tip API ```lua io.tail(filepath: , lines: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 文件路径字符串 | | lines | 要读取的行数 | #### 用法说明 读取文件尾部指定行数的数据,并显示,类似`cat xxx.txt | tail -n 10`命令,例如: ```lua -- 显示文件最后10行内容 io.tail("xxx.txt", 10) ``` ## io.cat * 读取和显示文件的所有内容 #### 函数原型 ::: tip API ```lua io.cat(filepath: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 文件路径字符串 | #### 用法说明 读取文件的所有内容并显示,类似`cat xxx.txt`命令,例如: ```lua io.cat("xxx.txt") ``` ## io.print * 带换行格式化输出内容到文件 #### 函数原型 ::: tip API ```lua io.print(content: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | content | 输出内容字符串 | #### 用法说明 直接格式化传参输出一行字符串到文件,并且带换行,例如: ```lua io.print("xxx.txt", "hello %s!", "xmake") ``` ## io.printf * 无换行格式化输出内容到文件 #### 函数原型 ::: tip API ```lua io.printf(format: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | format | 格式字符串 | | ... | 可变参数 | #### 用法说明 直接格式化传参输出一行字符串到文件,不带换行,例如: ```lua io.printf("xxx.txt", "hello %s!\n", "xmake") ``` ## io.lines * 读取文件的所有行 #### 函数原型 ::: tip API ```lua io.lines(filepath: , mode: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 文件路径字符串 | | mode | 读取模式字符串 | #### 用法说明 根据文件名返回该文件的所有行的内容,返回一个迭代器函数。 ```lua for line in io.lines("xxx.txt") do print(line) end ``` 也可以转换为数组: ```lua local lines = table.to_array(io.lines("xxx.txt")) ``` 支持与 [io.readfile](#io-readfile) 相同的选项参数: ```lua -- 以二进制模式读取每一行(保留 CRLF) for line in io.lines("xxx.txt", {encoding = "binary"}) do print(line) end -- 处理行继续符 for line in io.lines("xxx.txt", {continuation = "\\"}) do print(line) -- 以 \ 结尾的行会与下一行合并 end ``` 默认情况下,每行会去除换行符。如果需要保留换行符,使用 `file:read("L")` 方式。 ## io.stdfile * 获取标准输入输出文件 #### 函数原型 ::: tip API ```lua io.stdfile() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 根据文件名返回标准输入输出文件 ```lua -- 标准输入 io.stdin -- 标准输出 io.stdout -- 标准错误 io.stderr ``` ## io.openlock * 创建一把文件锁 #### 函数原型 ::: tip API ```lua io.openlock(filepath: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 文件路径字符串 | #### 用法说明 为给定的文件返回一个文件锁对象 ```lua local lock = io.openlock("xxx.txt") lock:lock() lock:unlock() lock:close() ``` ## io.replace * 根据表达式替换文件内容 #### 函数原型 ::: tip API ```lua io.replace(filepath: , pattern: , replace: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 文件路径字符串 | | pattern | 用于匹配的模式字符串 | | replace | 替换字符串或函数 | | options | 选项表(可选) | #### 用法说明 根据表达式和参数对文件进行全文替换 ```lua io.replace(filepath, pattern, replace, opt) io.replace("xxx.txt", "test", "xmake", { plain = true, encoding = "UTF-8" }) io.replace("xxx.txt", "%d[^\n]*", "xmake") ``` 关于参数 `opt` 成员的解释: > .plain: 若为 true,使用pattern进行简单匹配;为 false,则进行模式匹配; > > .encoding: 指定文件编码格式 --- --- url: /api/scripts/builtin-modules/ipairs.md --- # ipairs * for traversing arrays #### Function Prototype ::: tip API ```lua ipairs(t:
, f: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | t | Table to traverse | | f | Function to process values (optional) | | ... | Variable arguments for function (optional) | #### Usage 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](/api/scripts/builtin-modules/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 ``` --- --- url: /zh/api/scripts/builtin-modules/ipairs.md --- # ipairs * 用于遍历数组 #### 函数原型 ::: tip API ```lua ipairs(t:
, f: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | t | 要遍历的表 | | f | 处理值的函数(可选) | | ... | 函数的可变参数(可选) | #### 用法说明 这个是lua原生的内置api,在xmake中,在原有的行为上对其进行了一些扩展,来简化一些日常的lua遍历代码。 先看下默认的原生写法: ```lua for idx, val in ipairs({"a", "b", "c", "d", "e", "f"}) do print("%d %s", idx, val) end ``` 扩展写法类似[pairs](/zh/api/scripts/builtin-modules/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 ``` --- --- url: /api/scripts/extension-modules/lib/detect.md --- # lib.detect This module provides very powerful probing capabilities for probing programs, compilers, language features, dependencies, and more. ::: tip NOTE The interface of this module is spread across multiple module directories, try to import it by importing a single interface, which is more efficient. ::: ## detect.find\_file * Find files #### Function Prototype ::: tip API ```lua find_file(file: , paths:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | file | Required. File name or path | | paths | Required. Search path list | | opt | Optional. Option parameters, supports the following:- `suffixes` - Subdirectory suffix list | #### Return Value | Type | Description | |------|-------------| | string | Returns file path if found, nil if not found | #### Usage This interface provides a more powerful project than [os.files](/api/scripts/builtin-modules/os#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](/api/scripts/builtin-modules/os#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. ::: tip NOTE 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 #### Function Prototype ::: tip API ```lua find_path(file: , paths:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | file | Required. File or directory path | | paths | Required. Search path list | | opt | Optional. Option parameters, supports the following:- `suffixes` - Subdirectory suffix | #### Return Value | Type | Description | |------|-------------| | string | Returns path if found, nil if not found | #### Usage 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 #### Function Prototype ::: tip API ```lua find_library(name: , paths:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Library name | | paths | Required. Search path list | | opt | Optional. Option parameters, supports the following:- `kind` - Library type, static or shared- `suffixes` - Subdirectory suffix | #### Return Value | Type | Description | |------|-------------| | table | Returns library info table (contains filename, linkdir, link, kind), nil if not found | #### Usage 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 #### Function Prototype ::: tip API ```lua find_program(name: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Program name | | opt | Optional. Option parameters, supports the following:- `paths` - Search path list- `check` - Check command or function | #### Return Value | Type | Description | |------|-------------| | string | Returns program path if found, nil if not found | #### Usage 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", {paths = {"/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", {paths = {"/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", {paths = {"$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger)"}}) local program = find_program("ccache", {paths = {"$(env PATH)", function () return "/usr/local/bin" end}}) ``` ::: tip NOTE 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 #### Function Prototype ::: tip API ```lua find_programver(name: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Program name | | opt | Optional. Option parameters, supports the following:- `command` - Version command or function- `parse` - Version parsing rule or function | #### Return Value | Type | Description | |------|-------------| | string | Returns version number if found, nil if not found | #### Usage ```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}) ``` ::: tip NOTE 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 ::: warning NOTE After 2.6.x this interface is not recommended for direct use (internal use only), for library integration, please use `add_requires()` and `add_packages()` as much as possible. ::: ## detect.find\_tool * Find tool #### Function Prototype ::: tip API ```lua find_tool(toolname: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | toolname | Required. Tool name | | opt | Optional. Option parameters, supports the following:- `program` - Program command- `version` - Whether to get version- `paths` - Search paths- `check` - Check command or function | #### Return Value | Type | Description | |------|-------------| | table | Returns tool info table (contains name, program, version), nil if not found | #### Usage 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, paths = {"/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. ::: tip NOTE 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 #### Function Prototype ::: tip API ```lua find_toolname(program: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | program | Required. Program command or path | #### Return Value | Type | Description | |------|-------------| | string | Returns tool name | #### Usage 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 #### Function Prototype ::: tip API ```lua find_cudadevices(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters, supports the following:- `skip_compute_mode_prohibited` - Skip compute mode prohibited devices- `min_sm_arch` - Minimum SM architecture- `order_by_flops` - Order by performance | #### Return Value | Type | Description | |------|-------------| | table | Returns CUDA device list | #### Usage 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 #### Function Prototype ::: tip API ```lua features(toolname: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | toolname | Required. Tool name | | opt | Optional. Option parameters, supports the following:- `flags` - Flag list- `program` - Program command | #### Return Value | Type | Description | |------|-------------| | table | Returns feature list | #### Usage This interface is similar to [compiler.features](/api/scripts/extension-modules/core/tool/compiler#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](/api/scripts/extension-modules/core/tool/compiler#compiler-features). ## detect.has\_features * Determine if the specified feature is supported #### Function Prototype ::: tip API ```lua has_features(toolname: , features: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | toolname | Required. Tool name | | features | Required. Feature name or feature list | | opt | Optional. Option parameters, supports the following:- `flags` - Flag list- `program` - Program command | #### Return Value | Type | Description | |------|-------------| | boolean|table | Returns boolean for single feature, returns supported feature sublist for list | #### Usage This interface is similar to [compiler.has\_features](/api/scripts/extension-modules/core/tool/compiler#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](/api/scripts/extension-modules/core/tool/compiler#compiler-features). ## detect.has\_flags * Determine if the specified parameter option is supported #### Function Prototype ::: tip API ```lua has_flags(toolname: , flags: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | toolname | Required. Tool name | | flags | Required. Flag or flag list | | opt | Optional. Option parameters, supports the following:- `program` - Program command- `toolkind` - Tool type | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if supported, false otherwise | #### Usage This interface is similar to [compiler.has\_flags](/api/scripts/extension-modules/core/tool/compiler#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 #### Function Prototype ::: tip API ```lua has_cfuncs(funcs: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | funcs | Required. Function name or function list | | opt | Optional. Option parameters, supports the following:- `includes` - Include file list- `configs` - Config options- `target` - Target object- `verbose` - Whether to output verbosely | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if exists, false otherwise | #### Usage 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 = .., configs = {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 #### Function Prototype ::: tip API ```lua has_cxxfuncs(funcs: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | funcs | Required. Function name or function list | | opt | Optional. Option parameters, supports the following:- `includes` - Include file list- `configs` - Config options- `target` - Target object- `verbose` - Whether to output verbosely | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if exists, false otherwise | #### Usage 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 #### Function Prototype ::: tip API ```lua has_cincludes(includes: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Required. Include file name or include file list | | opt | Optional. Option parameters, supports the following:- `target` - Target object- `configs` - Config options | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if exists, false otherwise | #### Usage 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"}, {configs = {defines = "_GNU_SOURCE=1", languages = "cxx11"}}) ``` ## detect.has\_cxxincludes * Determine if the specified c++ header file exists #### Function Prototype ::: tip API ```lua has_cxxincludes(includes: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Required. Include file name or include file list | | opt | Optional. Option parameters, supports the following:- `target` - Target object- `configs` - Config options | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if exists, false otherwise | #### Usage This interface is similar to [lib.detect.has\_cincludes](#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 #### Function Prototype ::: tip API ```lua has_ctypes(types: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | Required. Type name or type list | | opt | Optional. Option parameters, supports the following:- `includes` - Include file list- `configs` - Config options | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if exists, false otherwise | #### Usage This interface is a simplified version of [lib.detect.check\_cxsnippets](#detect-check_cxsnippets) and is only used to detect types. ```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"}, configs = {"defines = "_GNU_SOURCE=1", languages = "cxx11"}}) ``` ## detect.has\_cxxtypes * Determine if the specified c++ type exists #### Function Prototype ::: tip API ```lua has_cxxtypes(types: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | Required. Type name or type list | | opt | Optional. Option parameters, supports the following:- `includes` - Include file list- `configs` - Config options | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if exists, false otherwise | #### Usage This interface is similar to [lib.detect.has\_ctypes](#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 #### Function Prototype ::: tip API ```lua check_cxsnippets(snippets: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | snippets | Required. Code snippet or code snippet list | | opt | Optional. Option parameters, supports the following:- `types` - Type list- `includes` - Include file list- `funcs` - Function list- `links` - Link library list- `target` - Target object- `sourcekind` - Source file type | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if compilation passes, false otherwise | #### Usage 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 try to compile it, if the compilation pass returns true. For some complex compiler features, even if [compiler.has\_features](/api/scripts/extension-modules/core/tool/compiler#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. --- --- url: /zh/api/scripts/extension-modules/lib/detect.md --- # lib.detect 此模块提供了非常强大的探测功能,用于探测程序、编译器、语言特性、依赖包等。 ::: tip 注意 此模块的接口分散在多个模块目录中,尽量通过导入单个接口来使用,这样效率更高。 ::: ## detect.find\_file * 查找文件 #### 函数原型 ::: tip API ```lua find_file(file: , paths:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | file | 必需。文件名或路径 | | paths | 必需。搜索路径列表 | | opt | 可选。选项参数,支持以下选项:- `suffixes` - 子目录后缀列表 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 找到文件返回文件路径,未找到返回 nil | #### 用法说明 这个接口提供了比[os.files](/zh/api/scripts/builtin-modules/os#os-files)更加强大的工程, 可以同时指定多个搜索目录,并且还能对每个目录指定附加的子目录, 来模式匹配查找,相当于是[os.files](/zh/api/scripts/builtin-modules/os#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 ``` 并且不用改变路径列表,就能动态切换子目录来搜索文件。 ::: tip 注意 我们也可以通过`xmake lua`插件来快速调用和测试此接口:`xmake lua lib.detect.find_file test.h /usr/local` ::: ## detect.find\_path * 查找路径 #### 函数原型 ::: tip API ```lua find_path(file: , paths:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | file | 必需。文件或目录路径 | | paths | 必需。搜索路径列表 | | opt | 可选。选项参数,支持以下选项:- `suffixes` - 子目录后缀 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 找到返回路径,未找到返回 nil | #### 用法说明 这个接口的用法跟[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 * 查找库文件 #### 函数原型 ::: tip API ```lua find_library(name: , paths:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。库名称 | | paths | 必需。搜索路径列表 | | opt | 可选。选项参数,支持以下选项:- `kind` - 库类型,static 或 shared- `suffixes` - 子目录后缀 | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回库信息表(包含 filename, linkdir, link, kind),未找到返回 nil | #### 用法说明 此接口用于指定的搜索目录中查找库文件(静态库,动态库),例如: ```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 * 查找可执行程序 #### 函数原型 ::: tip API ```lua find_program(name: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。程序名称 | | opt | 可选。选项参数,支持以下选项:- `paths` - 搜索路径列表- `check` - 检查命令或函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 找到返回程序路径,未找到返回 nil | #### 用法说明 这个接口比[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", {paths = {"/usr/bin", "/usr/local/bin"}, check = "--help"}) ``` 上述代码会尝试运行:`/usr/bin/ccache --help`,如果运行成功,则返回:`/usr/bin/ccache`。 如果`--help`也没法满足需求,有些程序没有`--version/--help`参数,那么可以自定义运行脚本,来运行检测: ```lua local program = find_program("ccache", {paths = {"/usr/bin", "/usr/local/bin"}, check = function (program) os.run("%s -h", program) end}) ``` 同样,搜索路径列表支持内建变量和自定义脚本: ```lua local program = find_program("ccache", {paths = {"$(env PATH)", "$(reg HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger)"}}) local program = find_program("ccache", {paths = {"$(env PATH)", function () return "/usr/local/bin" end}}) ``` ::: tip 注意 为了加速频发查找的效率,此接口是默认自带cache的,所以就算频繁查找相同的程序,也不会花太多时间。 如果要禁用cache,可以在工程目录执行`xmake f -c`清除本地cache。 ::: 我们也可以通过`xmake lua lib.detect.find_program ccache` 来快速测试。 ## detect.find\_programver * 查找可执行程序版本号 #### 函数原型 ::: tip API ```lua find_programver(name: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。程序名称 | | opt | 可选。选项参数,支持以下选项:- `command` - 版本命令或函数- `parse` - 版本解析规则或函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 找到返回版本号,未找到返回 nil | #### 用法说明 ```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}) ``` ::: tip 注意 为了加速频发查找的效率,此接口是默认自带cache的,如果要禁用cache,可以在工程目录执行`xmake f -c`清除本地cache。 ::: 我们也可以通过`xmake lua lib.detect.find_programver ccache` 来快速测试。 ## detect.find\_package * 查找包文件 ::: warning 注意 2.6.x 之后,这个接口不推荐直接使用(仅供内部使用),库集成,请尽量使用 `add_requires()` 和 `add_packages()`。 ::: ## detect.find\_tool * 查找工具 #### 函数原型 ::: tip API ```lua find_tool(toolname: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | toolname | 必需。工具名称 | | opt | 可选。选项参数,支持以下选项:- `program` - 程序命令- `version` - 是否获取版本- `paths` - 搜索路径- `check` - 检查命令或函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回工具信息表(包含 name, program, version),未找到返回 nil | #### 用法说明 此接口也是用于查找可执行程序,不过比[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, paths = {"/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`就可以查找到了。 ::: tip 注意 为了加速频发查找的效率,此接口是默认自带cache的,如果要禁用cache,可以在工程目录执行`xmake f -c`清除本地cache。 ::: 我们也可以通过`xmake lua lib.detect.find_tool clang` 来快速测试。 ## detect.find\_toolname * 查找工具名 #### 函数原型 ::: tip API ```lua find_toolname(program: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | program | 必需。程序命令或路径 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回工具名 | #### 用法说明 通过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 设备 #### 函数原型 ::: tip API ```lua find_cudadevices(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数,支持以下选项:- `skip_compute_mode_prohibited` - 跳过计算模式禁止的设备- `min_sm_arch` - 最小SM架构- `order_by_flops` - 按性能排序 | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回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 * 获取指定工具的所有特性 #### 函数原型 ::: tip API ```lua features(toolname: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | toolname | 必需。工具名称 | | opt | 可选。选项参数,支持以下选项:- `flags` - 标志列表- `program` - 程序命令 | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回特性列表 | #### 用法说明 此接口跟[compiler.features](/zh/api/scripts/extension-modules/core/tool/compiler#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](/zh/api/scripts/extension-modules/core/tool/compiler#compiler-features)。 ## detect.has\_features * 判断指定特性是否支持 #### 函数原型 ::: tip API ```lua has_features(toolname: , features: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | toolname | 必需。工具名称 | | features | 必需。特性名或特性列表 | | opt | 可选。选项参数,支持以下选项:- `flags` - 标志列表- `program` - 程序命令 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean|table | 如果传入单个特性返回boolean,传入列表返回支持的特性子列表 | #### 用法说明 此接口跟[compiler.has\_features](/zh/api/scripts/extension-modules/core/tool/compiler#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](/zh/api/scripts/extension-modules/core/tool/compiler#compiler-features)。 ## detect.has\_flags * 判断指定参数选项是否支持 #### 函数原型 ::: tip API ```lua has_flags(toolname: , flags: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | toolname | 必需。工具名称 | | flags | 必需。标志或标志列表 | | opt | 可选。选项参数,支持以下选项:- `program` - 程序命令- `toolkind` - 工具类型 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持返回 true,否则返回 false | #### 用法说明 此接口跟[compiler.has\_flags](/zh/api/scripts/extension-modules/core/tool/compiler#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函数是否存在 #### 函数原型 ::: tip API ```lua has_cfuncs(funcs: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | funcs | 必需。函数名或函数列表 | | opt | 可选。选项参数,支持以下选项:- `includes` - 头文件列表- `configs` - 配置选项- `target` - 目标对象- `verbose` - 是否详细输出 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 存在返回 true,否则返回 false | #### 用法说明 此接口是[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 = .., configs = {linkdirs = .., links = .., defines = ..}} ``` 其中verbose用于回显检测信息,target用于在检测前追加target中的配置信息, 而config用于自定义配置跟target相关的编译选项。 ## detect.has\_cxxfuncs * 判断指定c++函数是否存在 #### 函数原型 ::: tip API ```lua has_cxxfuncs(funcs: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | funcs | 必需。函数名或函数列表 | | opt | 可选。选项参数,支持以下选项:- `includes` - 头文件列表- `configs` - 配置选项- `target` - 目标对象- `verbose` - 是否详细输出 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 存在返回 true,否则返回 false | #### 用法说明 此接口跟[lib.detect.has\_cfuncs](#detect-has_cfuncs)类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++函数。 ## detect.has\_cincludes * 判断指定c头文件是否存在 #### 函数原型 ::: tip API ```lua has_cincludes(includes: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 必需。头文件名或头文件列表 | | opt | 可选。选项参数,支持以下选项:- `target` - 目标对象- `configs` - 配置选项 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 存在返回 true,否则返回 false | #### 用法说明 此接口是[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"}, {configs = {defines = "_GNU_SOURCE=1", languages = "cxx11"}}) ``` ## detect.has\_cxxincludes * 判断指定c++头文件是否存在 #### 函数原型 ::: tip API ```lua has_cxxincludes(includes: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 必需。头文件名或头文件列表 | | opt | 可选。选项参数,支持以下选项:- `target` - 目标对象- `configs` - 配置选项 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 存在返回 true,否则返回 false | #### 用法说明 此接口跟[lib.detect.has\_cincludes](#detect-has_cincludes)类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++头文件。 ## detect.has\_ctypes * 判断指定c类型是否存在 #### 函数原型 ::: tip API ```lua has_ctypes(types: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 必需。类型名或类型列表 | | opt | 可选。选项参数,支持以下选项:- `includes` - 头文件列表- `configs` - 配置选项 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 存在返回 true,否则返回 false | #### 用法说明 此接口是[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"}, configs = {"defines = "_GNU_SOURCE=1", languages = "cxx11"}}) ``` ## detect.has\_cxxtypes * 判断指定c++类型是否存在 #### 函数原型 ::: tip API ```lua has_cxxtypes(types: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 必需。类型名或类型列表 | | opt | 可选。选项参数,支持以下选项:- `includes` - 头文件列表- `configs` - 配置选项 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 存在返回 true,否则返回 false | #### 用法说明 此接口跟[lib.detect.has\_ctypes](#detect-has_ctypes)类似,请直接参考它的使用说明,唯一区别是这个接口用于检测c++类型。 ## detect.check\_cxsnippets * 检测c/c++代码片段是否能够编译通过 #### 函数原型 ::: tip API ```lua check_cxsnippets(snippets: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | snippets | 必需。代码片段或代码片段列表 | | opt | 可选。选项参数,支持以下选项:- `types` - 类型列表- `includes` - 头文件列表- `funcs` - 函数列表- `links` - 链接库列表- `target` - 目标对象- `sourcekind` - 源文件类型 | | | | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 编译通过返回 true,否则返回 false | #### 用法说明 通用的c/c++代码片段检测接口,通过传入多个代码片段列表,它会自动生成一个编译文件,然后尝试对它进行编译,如果编译通过返回true。 对于一些复杂的编译器特性,连[compiler.has\_features](/zh/api/scripts/extension-modules/core/tool/compiler#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++代码来检测。 --- --- url: /api/scripts/extension-modules/lib/lua/package.md --- # lib.lua.package This module provides access to native Lua package interfaces for loading dynamic libraries and Lua modules. ::: tip TIP To use this module, you need to import it first: `import("lib.lua.package")` ::: Xmake restricts access to native Lua modules and interfaces by default for safety reasons. This module provides access to some of the APIs provided by Lua when needed. ## package.loadlib * Load Lua module from dynamic library #### Function Prototype ::: tip API ```lua package.loadlib(libfile: , symbol: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | libfile | Required. The dynamic library file path (e.g., foo.dll, libfoo.so, libfoo.dylib) | | symbol | Required. The export symbol name (e.g., luaopen\_xxx) | #### Return Value | Type | Description | |------|-------------| | function | Returns a function to initialize the module | #### Usage This is typically used in high-performance scenarios where you need to load Lua modules from native dynamic libraries. ```lua import("lib.lua.package") -- Load the module from dynamic library local script = package.loadlib("/xxx/libfoo.so", "luaopen_mymodule") -- Initialize and use the module local mymodule = script() mymodule.hello() ``` --- --- url: /zh/api/scripts/extension-modules/lib/lua/package.md --- # lib.lua.package 此模块提供访问原生 Lua 包接口,用于加载动态库和 Lua 模块。 ::: tip 提示 使用此模块需要先导入:`import("lib.lua.package")` ::: 出于安全考虑,xmake 默认限制访问原生 Lua 模块和接口。此模块按需提供对 Lua 所提供 API 的访问。 ## package.loadlib * 从动态库加载 Lua 模块 #### 函数原型 ::: tip API ```lua package.loadlib(libfile: , symbol: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | libfile | 必需。动态库文件路径(例如:foo.dll, libfoo.so, libfoo.dylib) | | symbol | 必需。导出符号名称(例如:luaopen\_xxx) | #### 返回值说明 | 类型 | 描述 | |------|------| | function | 返回一个初始化模块的函数 | #### 用法说明 此功能通常用于高性能场景,需要从原生动态库加载 Lua 模块。 ```lua import("lib.lua.package") -- 从动态库加载模块 local script = package.loadlib("/xxx/libfoo.so", "luaopen_mymodule") -- 初始化并使用模块 local mymodule = script() mymodule.hello() ``` --- --- url: /api/scripts/builtin-modules/linuxos.md --- # linuxos The linux system operation module is a built-in module, no need to use [import](/api/scripts/builtin-modules/import) to import, and its interface can be called directly in the script scope. ## linuxos.name * Get linux system name #### Function Prototype ::: tip API ```lua linuxos.name() ``` ::: #### Parameter Description No parameters required for this function. #### Usage We can also quickly get the view through the following command ```sh xmake l linuxos.name ``` Some names currently supported are: * ubuntu * debian * archlinux * manjaro * linuxmint * centos * fedora * opensuse ## linuxos.version * Get linux system version #### Function Prototype ::: tip API ```lua linuxos.version() ``` ::: #### Parameter Description No parameters required for this function. #### Usage The version returned is the semver semantic version object ```lua if linux.version():ge("10.0") then -- ... end ``` ## linuxos.kernelver * Get linux system kernel version #### Function Prototype ::: tip API ```lua linuxos.kernelver() ``` ::: #### Parameter Description No parameters required for this function. #### Usage What is returned is also a semantic version object, you can also execute `xmake l linuxos.kernelver` to quickly view. --- --- url: /zh/api/scripts/builtin-modules/linuxos.md --- # linuxos Linux 系统操作模块,属于内置模块,无需使用[import](/zh/api/scripts/builtin-modules/import)导入,可直接脚本域调用其接口。 ## linuxos.name * 获取 linux 系统发行版名称 #### 函数原型 ::: tip API ```lua linuxos.name() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 我们也可以通过下面的命令,快速获取查看 ```sh xmake l linuxos.name ``` 目前支持的一些名称有: * ubuntu * debian * archlinux * manjaro * linuxmint * centos * fedora * opensuse ## linuxos.version * 获取 linux 系统版本 #### 函数原型 ::: tip API ```lua linuxos.version() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回的版本是 semver 语义版本对象 ```lua if linux.version():ge("10.0") then -- ... end ``` ## linuxos.kernelver * 获取 linux 系统内核版本 #### 函数原型 ::: tip API ```lua linuxos.kernelver() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回的也是语义版本对象,也可以执行 `xmake l linuxos.kernelver` 快速查看 --- --- url: /api/scripts/extension-modules/core/base/list.md --- # list The list module provides a doubly linked list data structure that supports efficient insertion and deletion operations at both ends. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.list")` ::: ## list.new * Create an empty linked list #### Function Prototype ::: tip API ```lua list.new() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a new empty doubly linked list object. ```lua local l = list.new() print(l:size()) -- Output: 0 print(l:empty()) -- Output: true ``` ## list:push * Add element to the end of the list #### Function Prototype ::: tip API ```lua list:push(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. The element to add | #### Usage Adds an element to the end of the list. Equivalent to `list:insert_last(item)`. This is the most common way to add elements: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:size()) -- Output: 3 print(l:first().value) -- Output: 1 print(l:last().value) -- Output: 3 ``` ::: tip TIP The list stores references to elements, not copies. You can store values of any type (numbers, strings, tables, etc.). ::: ## list:pop * Remove element from the end of the list #### Function Prototype ::: tip API ```lua list:pop() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes an element from the end of the list. Equivalent to `list:remove_last()`. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:pop() print(l:last().value) -- Output: 2 ``` ## list:unshift * Add element to the beginning of the list #### Function Prototype ::: tip API ```lua list:unshift(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. The element to add | #### Usage Adds an element to the beginning of the list. Equivalent to `list:insert_first(item)`. ```lua local l = list.new() l:push({value = 2}) l:push({value = 3}) l:unshift({value = 1}) for item in l:items() do print(item.value) -- Output: 1, 2, 3 end ``` ## list:shift * Remove element from the beginning of the list #### Function Prototype ::: tip API ```lua list:shift() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes an element from the beginning of the list. Equivalent to `list:remove_first()`. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:shift() print(l:first().value) -- Output: 2 ``` ## list:insert * Insert an element #### Function Prototype ::: tip API ```lua list:insert(item: , after: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. The element to insert | | after | Optional. Insert after this element; if nil, inserts at the end | #### Usage Inserts a new element after the specified element. If the `after` parameter is not provided, inserts at the end of the list. ```lua local l = list.new() local v3 = {value = 3} l:insert({value = 1}) l:insert({value = 2}) l:insert(v3) l:insert({value = 5}) l:insert({value = 4}, v3) -- Insert {value = 4} after v3 local idx = 1 for item in l:items() do print(item.value) -- Output: 1, 2, 3, 4, 5 idx = idx + 1 end ``` ## list:insert\_first * Insert element at the beginning #### Function Prototype ::: tip API ```lua list:insert_first(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. The element to insert | #### Usage Inserts an element at the beginning of the list. Used for scenarios requiring insertion at the head: ```lua local l = list.new() l:push({value = 2}) l:push({value = 3}) l:insert_first({value = 1}) print(l:first().value) -- Output: 1 ``` ## list:insert\_last * Insert element at the end #### Function Prototype ::: tip API ```lua list:insert_last(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. The element to insert | #### Usage Inserts an element at the end of the list. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:insert_last({value = 3}) print(l:last().value) -- Output: 3 ``` ## list:remove * Remove specified element #### Function Prototype ::: tip API ```lua list:remove(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. The element to remove | #### Usage Removes the specified element from the list and returns the removed element. ```lua local l = list.new() local v3 = {value = 3} l:push({value = 1}) l:push({value = 2}) l:push(v3) l:push({value = 4}) l:push({value = 5}) l:remove(v3) print(l:size()) -- Output: 4 ``` Safe way to remove elements during iteration: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) -- Iterate and remove all elements local item = l:first() while item ~= nil do local next = l:next(item) l:remove(item) item = next end print(l:empty()) -- Output: true ``` ## list:remove\_first * Remove element from the beginning #### Function Prototype ::: tip API ```lua list:remove_first() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes an element from the beginning of the list and returns the removed element. Returns nil if the list is empty. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:remove_first() print(l:first().value) -- Output: 2 ``` ## list:remove\_last * Remove element from the end #### Function Prototype ::: tip API ```lua list:remove_last() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes an element from the end of the list and returns the removed element. Returns nil if the list is empty. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:remove_last() print(l:last().value) -- Output: 2 ``` ## list:first * Get the first element #### Function Prototype ::: tip API ```lua list:first() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the first element of the list without removing it. Returns nil if the list is empty. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:first().value) -- Output: 1 ``` ## list:last * Get the last element #### Function Prototype ::: tip API ```lua list:last() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the last element of the list without removing it. Returns nil if the list is empty. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:last().value) -- Output: 3 ``` ## list:next * Get the next element #### Function Prototype ::: tip API ```lua list:next(current: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | current | Optional. The current element; if nil, returns the first element | #### Usage Gets the next element after the specified element. If `current` is nil, returns the first element. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local first = l:first() local second = l:next(first) print(second.value) -- Output: 2 ``` Used for manual iteration: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local item = l:next(nil) -- Get the first element while item do print(item.value) item = l:next(item) end ``` ## list:prev * Get the previous element #### Function Prototype ::: tip API ```lua list:prev(current: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | current | Optional. The current element; if nil, returns the last element | #### Usage Gets the previous element before the specified element. If `current` is nil, returns the last element. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local last = l:last() local second = l:prev(last) print(second.value) -- Output: 2 ``` Used for reverse iteration: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local item = l:prev(nil) -- Get the last element while item do print(item.value) -- Output: 3, 2, 1 item = l:prev(item) end ``` ## list:size * Get list size #### Function Prototype ::: tip API ```lua list:size() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the number of elements in the list. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:size()) -- Output: 3 ``` ## list:empty * Check if list is empty #### Function Prototype ::: tip API ```lua list:empty() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns true if the list is empty (contains no elements). ```lua local l = list.new() print(l:empty()) -- Output: true l:push({value = 1}) print(l:empty()) -- Output: false ``` ## list:clear * Clear the list #### Function Prototype ::: tip API ```lua list:clear() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes all elements from the list, resetting it to an empty list. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:clear() print(l:empty()) -- Output: true print(l:size()) -- Output: 0 ``` ## list:items * Iterate forward through the list #### Function Prototype ::: tip API ```lua list:items() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an iterator function for traversing all elements in the list from head to tail. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) for item in l:items() do print(item.value) -- Output: 1, 2, 3 end ``` Verify order: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:push({value = 4}) l:push({value = 5}) local idx = 1 for item in l:items() do assert(item.value == idx) idx = idx + 1 end ``` ## list:ritems * Iterate backward through the list #### Function Prototype ::: tip API ```lua list:ritems() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an iterator function for traversing all elements in the list from tail to head. ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) for item in l:ritems() do print(item.value) -- Output: 3, 2, 1 end ``` Reverse iteration with deletion: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:push({value = 4}) l:push({value = 5}) local item = l:last() while item ~= nil do local prev = l:prev(item) l:remove(item) item = prev end print(l:empty()) -- Output: true ``` ::: tip TIP The list module implements a doubly linked list, providing O(1) time complexity for insertion and deletion operations at both ends. Suitable for scenarios requiring frequent operations at both ends, such as queues, stacks, LRU caches, etc. ::: ::: warning WARNING * The list stores references to elements; modifying elements will affect the content in the list * When deleting elements during iteration, be careful to first obtain the reference to the next/previous element * The time complexity of insertion or deletion operations is O(1) (if you have the element reference) ::: --- --- url: /zh/api/scripts/extension-modules/core/base/list.md --- # list list 模块提供了双向链表数据结构,支持高效的头尾插入删除操作。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.list")` ::: ## list.new * 创建空的链表 #### 函数原型 ::: tip API ```lua list.new() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建一个新的空双向链表对象。 ```lua local l = list.new() print(l:size()) -- 输出: 0 print(l:empty()) -- 输出: true ``` ## list:push * 在链表尾部添加元素 #### 函数原型 ::: tip API ```lua list:push(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 要添加的元素 | #### 用法说明 在链表的尾部添加一个元素。等同于 `list:insert_last(item)`。 这是最常用的添加元素方式: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:size()) -- 输出: 3 print(l:first().value) -- 输出: 1 print(l:last().value) -- 输出: 3 ``` ::: tip 提示 链表存储的是对元素的引用,而不是副本。可以存储任意类型的值(数字、字符串、table 等)。 ::: ## list:pop * 从链表尾部移除元素 #### 函数原型 ::: tip API ```lua list:pop() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 从链表的尾部移除一个元素。等同于 `list:remove_last()`。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:pop() print(l:last().value) -- 输出: 2 ``` ## list:unshift * 在链表头部添加元素 #### 函数原型 ::: tip API ```lua list:unshift(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 要添加的元素 | #### 用法说明 在链表的头部添加一个元素。等同于 `list:insert_first(item)`。 ```lua local l = list.new() l:push({value = 2}) l:push({value = 3}) l:unshift({value = 1}) for item in l:items() do print(item.value) -- 输出: 1, 2, 3 end ``` ## list:shift * 从链表头部移除元素 #### 函数原型 ::: tip API ```lua list:shift() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 从链表的头部移除一个元素。等同于 `list:remove_first()`。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:shift() print(l:first().value) -- 输出: 2 ``` ## list:insert * 插入元素 #### 函数原型 ::: tip API ```lua list:insert(item: , after: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 要插入的元素 | | after | 可选,在此元素后插入,如果为 nil 则在尾部插入 | #### 用法说明 在指定元素后面插入新元素。如果不提供 `after` 参数,则在链表尾部插入。 ```lua local l = list.new() local v3 = {value = 3} l:insert({value = 1}) l:insert({value = 2}) l:insert(v3) l:insert({value = 5}) l:insert({value = 4}, v3) -- 在 v3 后插入 {value = 4} local idx = 1 for item in l:items() do print(item.value) -- 输出: 1, 2, 3, 4, 5 idx = idx + 1 end ``` ## list:insert\_first * 在链表头部插入元素 #### 函数原型 ::: tip API ```lua list:insert_first(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 要插入的元素 | #### 用法说明 在链表的头部插入一个元素。 用于需要在头部插入的场景: ```lua local l = list.new() l:push({value = 2}) l:push({value = 3}) l:insert_first({value = 1}) print(l:first().value) -- 输出: 1 ``` ## list:insert\_last * 在链表尾部插入元素 #### 函数原型 ::: tip API ```lua list:insert_last(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 要插入的元素 | #### 用法说明 在链表的尾部插入一个元素。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:insert_last({value = 3}) print(l:last().value) -- 输出: 3 ``` ## list:remove * 删除指定元素 #### 函数原型 ::: tip API ```lua list:remove(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 要删除的元素 | #### 用法说明 从链表中删除指定的元素,返回被删除的元素。 ```lua local l = list.new() local v3 = {value = 3} l:push({value = 1}) l:push({value = 2}) l:push(v3) l:push({value = 4}) l:push({value = 5}) l:remove(v3) print(l:size()) -- 输出: 4 ``` 在遍历时删除元素的安全方式: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) -- 遍历并删除所有元素 local item = l:first() while item ~= nil do local next = l:next(item) l:remove(item) item = next end print(l:empty()) -- 输出: true ``` ## list:remove\_first * 删除链表头部元素 #### 函数原型 ::: tip API ```lua list:remove_first() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 从链表头部删除一个元素,返回被删除的元素。如果链表为空,返回 nil。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:remove_first() print(l:first().value) -- 输出: 2 ``` ## list:remove\_last * 删除链表尾部元素 #### 函数原型 ::: tip API ```lua list:remove_last() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 从链表尾部删除一个元素,返回被删除的元素。如果链表为空,返回 nil。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:remove_last() print(l:last().value) -- 输出: 2 ``` ## list:first * 获取链表首元素 #### 函数原型 ::: tip API ```lua list:first() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回链表的第一个元素,不删除。如果链表为空,返回 nil。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:first().value) -- 输出: 1 ``` ## list:last * 获取链表尾元素 #### 函数原型 ::: tip API ```lua list:last() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回链表的最后一个元素,不删除。如果链表为空,返回 nil。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:last().value) -- 输出: 3 ``` ## list:next * 获取下一个元素 #### 函数原型 ::: tip API ```lua list:next(current: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | current | 当前元素,如果为 nil 则返回第一个元素 | #### 用法说明 获取指定元素的下一个元素。如果 `current` 为 nil,返回第一个元素。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local first = l:first() local second = l:next(first) print(second.value) -- 输出: 2 ``` 用于手动遍历链表: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local item = l:next(nil) -- 获取第一个元素 while item do print(item.value) item = l:next(item) end ``` ## list:prev * 获取上一个元素 #### 函数原型 ::: tip API ```lua list:prev(current: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | current | 当前元素,如果为 nil 则返回最后一个元素 | #### 用法说明 获取指定元素的上一个元素。如果 `current` 为 nil,返回最后一个元素。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local last = l:last() local second = l:prev(last) print(second.value) -- 输出: 2 ``` 用于反向遍历: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) local item = l:prev(nil) -- 获取最后一个元素 while item do print(item.value) -- 输出: 3, 2, 1 item = l:prev(item) end ``` ## list:size * 获取链表大小 #### 函数原型 ::: tip API ```lua list:size() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回链表中元素的个数。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) print(l:size()) -- 输出: 3 ``` ## list:empty * 判断链表是否为空 #### 函数原型 ::: tip API ```lua list:empty() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回 true 表示链表为空(不包含任何元素)。 ```lua local l = list.new() print(l:empty()) -- 输出: true l:push({value = 1}) print(l:empty()) -- 输出: false ``` ## list:clear * 清空链表 #### 函数原型 ::: tip API ```lua list:clear() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 删除链表中的所有元素,重置为空链表。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:clear() print(l:empty()) -- 输出: true print(l:size()) -- 输出: 0 ``` ## list:items * 正向遍历链表 #### 函数原型 ::: tip API ```lua list:items() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回一个迭代器函数,用于从头到尾遍历链表中的所有元素。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) for item in l:items() do print(item.value) -- 输出: 1, 2, 3 end ``` 验证顺序: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:push({value = 4}) l:push({value = 5}) local idx = 1 for item in l:items() do assert(item.value == idx) idx = idx + 1 end ``` ## list:ritems * 反向遍历链表 #### 函数原型 ::: tip API ```lua list:ritems() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回一个迭代器函数,用于从尾到头遍历链表中的所有元素。 ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) for item in l:ritems() do print(item.value) -- 输出: 3, 2, 1 end ``` 反向遍历并删除: ```lua local l = list.new() l:push({value = 1}) l:push({value = 2}) l:push({value = 3}) l:push({value = 4}) l:push({value = 5}) local item = l:last() while item ~= nil do local prev = l:prev(item) l:remove(item) item = prev end print(l:empty()) -- 输出: true ``` ::: tip 提示 list 模块实现的是双向链表,提供 O(1) 时间复杂度的头尾插入删除操作。适合需要频繁在两端操作的场景,如队列、栈、LRU 缓存等。 ::: ::: warning 注意 * 链表存储的是对元素的引用,修改元素会影响链表中的内容 * 遍历时删除元素需要注意先获取下一个/上一个元素的引用 * 插入或删除操作的时间复杂度为 O(1)(如果已有元素引用) ::: --- --- url: /api/scripts/extension-modules/core/compress/lz4.md --- # lz4 The lz4 module provides compression and decompression functions based on the LZ4 algorithm. It is an extension module of xmake. LZ4 is an extremely fast lossless compression algorithm, focusing on compression and decompression speed. ::: tip TIP To use this module, you need to import it first: `import("core.compress.lz4")` ::: ## lz4.compress * Compress data (frame format) #### Function Prototype ::: tip API ```lua lz4.compress(data: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Data to compress, can be a string or bytes object | #### Return Value | Type | Description | |------|-------------| | bytes | Compressed data (bytes object) | #### Usage Compresses data using the LZ4 frame format. Supports string or bytes object as input and returns a compressed bytes object. The frame format contains complete metadata and is self-contained, recommended for general scenarios. Compared to block format, it includes additional header information and checksums, requiring no extra information for decompression: ```lua import("core.compress.lz4") -- Compress string local compressed = lz4.compress("hello world") print("Original size: 11") print("Compressed size:", compressed:size()) -- Can also compress bytes object local bytes_data = bytes("hello world") local compressed = lz4.compress(bytes_data) ``` ## lz4.decompress * Decompress data (frame format) #### Function Prototype ::: tip API ```lua lz4.decompress(compressed: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | compressed | Required. Data compressed using LZ4 frame format (bytes object) | #### Return Value | Type | Description | |------|-------------| | bytes | Decompressed data (bytes object) | #### Usage Decompresses data compressed using the LZ4 frame format and returns a decompressed bytes object. Complete compression and decompression example: ```lua import("core.compress.lz4") local original = "hello world" local compressed = lz4.compress(original) local decompressed = lz4.decompress(compressed) assert(decompressed:str() == original) ``` ## lz4.block\_compress * Compress data (block format) #### Function Prototype ::: tip API ```lua lz4.block_compress(data: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Data to compress, can be a string or bytes object | #### Return Value | Type | Description | |------|-------------| | bytes | Compressed data (bytes object) | #### Usage Compresses data using the LZ4 block format. The block format is more lightweight, does not include frame header information, resulting in smaller compressed data, but requires managing and recording the original data size information yourself. Suitable for scenarios with higher compression ratio requirements where you can manage metadata yourself. ```lua import("core.compress.lz4") -- Compress string local compressed = lz4.block_compress("hello world") -- Or compress bytes object local bytes_data = bytes("hello world") local compressed = lz4.block_compress(bytes_data) ``` ## lz4.block\_decompress * Decompress data (block format) #### Function Prototype ::: tip API ```lua lz4.block_decompress(compressed: , realsize: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | compressed | Required. Data compressed using LZ4 block format (bytes object) | | realsize | Required. Size of the original data | #### Return Value | Type | Description | |------|-------------| | bytes | Decompressed data (bytes object) | #### Usage Decompresses data compressed using the LZ4 block format. The original data size `realsize` must be provided. Block format compression and decompression example: ```lua import("core.compress.lz4") local original = "hello world" local original_size = #original -- Compress local compressed = lz4.block_compress(original) -- Decompress with original size local decompressed = lz4.block_decompress(compressed, original_size) assert(decompressed:str() == original) ``` ## lz4.compress\_file * Compress a file #### Function Prototype ::: tip API ```lua lz4.compress_file(srcfile: , dstfile: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | srcfile | Required. Path to the source file to compress | | dstfile | Required. Path to the compressed destination file | #### Return Value This function has no return value. #### Usage Directly compresses a file using the LZ4 frame format. Compared to reading the entire file content and then compressing, this interface is more suitable for handling large files with lower memory usage: ```lua import("core.compress.lz4") lz4.compress_file("input.txt", "output.lz4") -- Compress build output local srcfile = "build/output.log" local dstfile = "build/output.log.lz4" lz4.compress_file(srcfile, dstfile) print("File compressed:", dstfile) ``` ## lz4.decompress\_file * Decompress a file #### Function Prototype ::: tip API ```lua lz4.decompress_file(srcfile: , dstfile: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | srcfile | Required. Path to the source file to decompress | | dstfile | Required. Path to the decompressed destination file | #### Return Value This function has no return value. #### Usage Directly decompresses a file and restores the original file content: ```lua import("core.compress.lz4") lz4.decompress_file("input.lz4", "output.txt") -- Decompress log file local srcfile = "build/output.log.lz4" local dstfile = "build/output.log" lz4.decompress_file(srcfile, dstfile) -- Read decompressed content local content = io.readfile(dstfile) print(content) ``` ## lz4.compress\_stream * Open a compression stream #### Function Prototype ::: tip API ```lua lz4.compress_stream() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | stream | Compression stream object supporting the following methods:- `stream:write(data, opt)` - Write data for compression- `stream:read(buffer, size, opt)` - Read compressed data | #### Usage Creates a compression stream object for streaming data compression. Streaming compression is suitable for the following scenarios: * Handling large files, compressing while reading, without loading all content at once * Real-time data compression, such as network streams * Scenarios requiring controlled memory usage Example: ```lua import("core.compress.lz4") local stream = lz4.compress_stream() local buffer = bytes(8192) -- Write data stream:write("hello ") stream:write("world", {beof = true}) -- Mark end on last write -- Read compression result local count, data = stream:read(buffer, 8192) if count > 0 then print("Compressed " .. count .. " bytes") end ``` ## lz4.decompress\_stream * Open a decompression stream #### Function Prototype ::: tip API ```lua lz4.decompress_stream() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | stream | Decompression stream object supporting the following methods:- `stream:write(data, opt)` - Write compressed data- `stream:read(buffer, size, opt)` - Read decompressed data | #### Usage Creates a decompression stream object for streaming data decompression. Streaming decompression example: ```lua import("core.compress.lz4") local stream = lz4.decompress_stream() local buffer = bytes(8192) -- Write compressed data stream:write(compressed_data, {beof = true}) -- Read decompression result local count, data = stream:read(buffer, 8192) if count > 0 then print("Decompressed:", data:str()) end ``` --- --- url: /zh/api/scripts/extension-modules/core/compress/lz4.md --- # lz4 lz4 模块提供了基于 LZ4 算法的压缩和解压功能,属于 xmake 的扩展模块。 LZ4 是一个非常快速的无损压缩算法,专注于压缩和解压速度。 ::: tip 提示 使用此模块需要先导入:`import("core.compress.lz4")` ::: ## lz4.compress * 压缩数据(帧格式) #### 函数原型 ::: tip API ```lua lz4.compress(data: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。要压缩的数据,可以是字符串或 bytes 对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | bytes | 压缩后的数据(bytes 对象) | #### 用法说明 使用 LZ4 帧格式压缩数据,支持字符串或 bytes 对象作为输入,返回压缩后的 bytes 对象。 帧格式包含完整的元数据,自包含,推荐用于一般场景。相比块格式,它包含额外的头信息和校验和,解压时不需要额外信息: ```lua import("core.compress.lz4") -- 压缩字符串 local compressed = lz4.compress("hello world") print("原始大小: 11") print("压缩后大小:", compressed:size()) -- 也可以压缩 bytes 对象 local bytes_data = bytes("hello world") local compressed = lz4.compress(bytes_data) ``` ## lz4.decompress * 解压数据(帧格式) #### 函数原型 ::: tip API ```lua lz4.decompress(compressed: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | compressed | 必需。使用 LZ4 帧格式压缩的数据(bytes 对象) | #### 返回值说明 | 类型 | 描述 | |------|------| | bytes | 解压后的数据(bytes 对象) | #### 用法说明 解压使用 LZ4 帧格式压缩的数据,返回解压后的 bytes 对象。 完整的压缩解压示例: ```lua import("core.compress.lz4") local original = "hello world" local compressed = lz4.compress(original) local decompressed = lz4.decompress(compressed) assert(decompressed:str() == original) ``` ## lz4.block\_compress * 压缩数据(块格式) #### 函数原型 ::: tip API ```lua lz4.block_compress(data: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。要压缩的数据,可以是字符串或 bytes 对象 | #### 返回值说明 | 类型 | 描述 | |------|------| | bytes | 压缩后的数据(bytes 对象) | #### 用法说明 使用 LZ4 块格式压缩数据。块格式更轻量,不包含帧头信息,压缩后的数据更小,但需要自行管理和记录原始数据大小信息。 适合对压缩率要求较高,且可以自行管理元数据的场景。 ```lua import("core.compress.lz4") -- 压缩字符串 local compressed = lz4.block_compress("hello world") -- 或压缩 bytes 对象 local bytes_data = bytes("hello world") local compressed = lz4.block_compress(bytes_data) ``` ## lz4.block\_decompress * 解压数据(块格式) #### 函数原型 ::: tip API ```lua lz4.block_decompress(compressed: , realsize: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | compressed | 必需。使用 LZ4 块格式压缩的数据(bytes 对象) | | realsize | 必需。原始数据的大小 | #### 返回值说明 | 类型 | 描述 | |------|------| | bytes | 解压后的数据(bytes 对象) | #### 用法说明 解压使用 LZ4 块格式压缩的数据,必须提供原始数据的大小 `realsize`。 块格式的压缩解压示例: ```lua import("core.compress.lz4") local original = "hello world" local original_size = #original -- 压缩 local compressed = lz4.block_compress(original) -- 解压时需要提供原始大小 local decompressed = lz4.block_decompress(compressed, original_size) assert(decompressed:str() == original) ``` ## lz4.compress\_file * 压缩文件 #### 函数原型 ::: tip API ```lua lz4.compress_file(srcfile: , dstfile: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | srcfile | 必需。要压缩的源文件路径 | | dstfile | 必需。压缩后的目标文件路径 | #### 返回值说明 此函数无返回值。 #### 用法说明 直接压缩文件,使用 LZ4 帧格式。相比一次性读取整个文件内容再压缩,这个接口更适合处理大文件,内存占用更低: ```lua import("core.compress.lz4") lz4.compress_file("input.txt", "output.lz4") -- 压缩构建输出 local srcfile = "build/output.log" local dstfile = "build/output.log.lz4" lz4.compress_file(srcfile, dstfile) print("文件已压缩:", dstfile) ``` ## lz4.decompress\_file * 解压文件 #### 函数原型 ::: tip API ```lua lz4.decompress_file(srcfile: , dstfile: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | srcfile | 必需。要解压的源文件路径 | | dstfile | 必需。解压后的目标文件路径 | #### 返回值说明 此函数无返回值。 #### 用法说明 直接解压文件,恢复原始文件内容: ```lua import("core.compress.lz4") lz4.decompress_file("input.lz4", "output.txt") -- 解压日志文件 local srcfile = "build/output.log.lz4" local dstfile = "build/output.log" lz4.decompress_file(srcfile, dstfile) -- 读取解压后的内容 local content = io.readfile(dstfile) print(content) ``` ## lz4.compress\_stream * 打开压缩流 #### 函数原型 ::: tip API ```lua lz4.compress_stream() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | stream | 压缩流对象,支持以下方法:- `stream:write(data, opt)` - 写入数据进行压缩- `stream:read(buffer, size, opt)` - 读取压缩后的数据 | #### 用法说明 创建一个压缩流对象,用于流式压缩数据。 流式压缩适合以下场景: * 处理大文件,边读边压缩,不需要一次性加载全部内容 * 实时数据压缩,如网络流 * 需要控制内存占用的场景 示例: ```lua import("core.compress.lz4") local stream = lz4.compress_stream() local buffer = bytes(8192) -- 写入数据 stream:write("hello ") stream:write("world", {beof = true}) -- 最后一次写入标记结束 -- 读取压缩结果 local count, data = stream:read(buffer, 8192) if count > 0 then print("压缩了 " .. count .. " 字节") end ``` ## lz4.decompress\_stream * 打开解压流 #### 函数原型 ::: tip API ```lua lz4.decompress_stream() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | stream | 解压流对象,支持以下方法:- `stream:write(data, opt)` - 写入压缩数据- `stream:read(buffer, size, opt)` - 读取解压后的数据 | #### 用法说明 创建一个解压流对象,用于流式解压数据。 流式解压示例: ```lua import("core.compress.lz4") local stream = lz4.decompress_stream() local buffer = bytes(8192) -- 写入压缩数据 stream:write(compressed_data, {beof = true}) -- 读取解压结果 local count, data = stream:read(buffer, 8192) if count > 0 then print("解压后:", data:str()) end ``` --- --- url: /api/scripts/builtin-modules/macos.md --- # macos The macOS system operation module is a built-in module, no need to use [import](/api/scripts/builtin-modules/import) to import, you can directly call its interface in the script scope. ## macos.version * Get macOS system version #### Function Prototype ::: tip API ```lua macos.version() ``` ::: #### Parameter Description No parameters required for this function. #### Usage The version returned is the semver semantic version object ```lua if macos.version():ge("10.0") then - ... end ``` --- --- url: /zh/api/scripts/builtin-modules/macos.md --- # macos macOS 系统操作模块,属于内置模块,无需使用[import](/zh/api/scripts/builtin-modules/import)导入,可直接脚本域调用其接口。 ## macos.version * 获取 macOS 系统版本 #### 函数原型 ::: tip API ```lua macos.version() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回的版本是 semver 语义版本对象 ```lua if macos.version():ge("10.0") then -- ... end ``` --- --- url: /guide/project-configuration/multi-level-directories.md --- # Multi-level Directories In the script field we can import various rich extension modules by import, and in the description field we can introduce the project subdirectory through the [includes](/api/description/global-interfaces.html#includes) interface. Remember: Xmake's includes handles the configuration relationship according to the tree structure. The target configuration in `xmake.lua` in the subdirectory inherits the root domain configuration in the parent `xmake.lua`, for example: Currently, there is the following project structure: ``` Projectdir - xmake.lua - src - xmake.lua ``` `projectdir/xmake.lua` is the project's root `xmake.lua` configuration, and `src/xmake.lua` is a sub-configuration of the project. `projectdir/xmake.lua` content: ```lua add_defines("ROOT") target("test1") set_kind("binary") add_files("src/*.c") add_defines("TEST1") target("test2") set_kind("binary") add_files("src/*.c") add_defines("TEST2") Includes("src") ``` The global root domain is configured with `add_defines("ROOT")`, which affects all target configurations below, including all target configurations in the `sub-xmake.lua` of includes, so this is the global total configuration. The `add_defines("TEST1")` and `add_defines("TEST2")` in test1/test2 belong to the local configuration and only take effect on the current target. `src/xmake.lua` content: ```lua add_defines("ROOT2") target("test3") set_kind("binary") add_files("src/*.c") add_defines("TEST3") ``` In the `src/xmake.lua` sub-configuration, there is also a global root domain, configured with `add_defines("ROOT2")`, which belongs to the sub-configuration root domain and only takes effect on all targets in the current `sub-xmake.lua`. For the target `xmake.lua` in the lower level includes the target, because as previously said, Xmake is the configuration inheritance relationship of the tree structure. Therefore, the final configuration results of these targets are: ``` target("test1"): -DROOT -DTEST1 target("test2"): -DROOT -DTEST2 target("test3"): -DROOT -DROOT2 -DTEST3 ``` --- --- url: /guide/project-configuration/namespace-isolation.md --- # Namespace Isolation {#namespace-isolation} If the user maintains two independent sub-projects, and there are some targets, options, and rule names with the same name, when they are integrated into one project through includes, there may be a naming conflict that causes compilation errors. To avoid this problem, Xmake provides the namespace feature to isolate different projects into independent namespaces, so that they can be built independently and merged without conflict, for example: ```lua [xmake.lua] add_rules("mode.debug", "mode.release") add_defines("ROOT") namespace("ns1", function () includes("foo") end) target("foo") set_kind("binary") add_deps("ns1::foo") add_files("src/main.cpp") add_defines("TEST") ``` ```lua [foo/xmake.lua] add_defines("NS1_ROOT") target("foo") set_kind("static") add_files("src/foo.cpp") add_defines("FOO") ``` In the above configuration, foo is an independent project directory and can be built separately. It is a library project. However, we can use `includes("foo")` to introduce it into another project for use. That project also has a foo target with the same name. Due to the isolation of namespaces, there will be no conflict between them. We can use `ns1::foo` to access the target in the foo subproject. In addition, the root scope configurations in the namespace will not affect each other. For more information on the use of namespaces, please refer to the full document: [Namespace API Manual](/api/description/global-interfaces#namespace). --- --- url: /api/scripts/native-modules.md --- # Native Modules We know that in xmake, you can import some lua modules through the import interface for use in the script domain. However, if the operation of some modules is time-consuming, then lua implementation is not an ideal choice. Therefore, in the new version, we have added support for the native lua module, which can be implemented through native to achieve speed-up optimization. Moreover, importing and using the module is as simple as the lua module. When using native modules, xmake will perform two stages of compilation. First, it will automatically compile the native module, and then import the module into lua as a library or binary. For users, they only need to call import to import. ## Define dynamic library module The advantage of the dynamic library module is that it not only achieves performance acceleration through native, but also avoids the creation of additional sub-processes for each call, making it more lightweight and further improving the speed. We can first define a dynamic library module, which fully supports all C APIs of Lua, so we can also directly introduce some third-party open source Lua native modules for use. Here we also have a complete example of importing the lua-cjson module for reference: [native\_module\_cjson](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/native_module_cjson) First, we first implement the shared native code, so the interface is exported through the lua API. ```js [.vitepress/config.js] export default { // site-level options title: 'VitePress', description: 'Just playing around.', themeConfig: { // theme-level options } } ``` ```c++ [./modules/foo/foo.c] #include static int c_add(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a + b); return 1; } static int c_sub(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a - b); return 1; } int luaopen(foo, lua_State* lua) { //Collect add and sub static const luaL_Reg funcs[] = { {"add", c_add}, {"sub", c_sub}, {NULL, NULL} }; lua_newtable(lua); // pass function list luaL_setfuncs(lua, funcs, 0); return 1; } ``` Notice here that we have included an interface header file of `xmi.h`. In fact, we can also directly introduce `lua.h` and `luaconf.h`. The effect is the same, but it will provide better cross-platform performance. , it will automatically handle the differences between lua/luajit and versions internally. Then, we configure `add_rules("modules.shared")` to compile as a shared native module without introducing any other dependencies. Even Lua dependencies do not need to be introduced, because the xmake main program has exported all Lua interfaces and can be used directly, so the entire module is very lightweight. ```lua [./modules/foo/xmake.lua] add_rules("mode.debug", "mode.release") target("foo") -- Specify the target as the library lua module add_rules("module.shared") add_files("foo.c") ``` ## Define binary module In addition to the dynamic library module, we also provide the import of another binary module. It is actually an executable file. Every time the module interface is called, a child process will be called. So what are the benefits of it? Although it is not as efficient as the dynamic library module, its module implementation is simpler. There is no need to call the lua API. It only needs to process the parameter data and output the return value through stdout. In addition, compared to binary distribution, it is distributed through source code, so it also solves the cross-platform problem. Whether to use a dynamic library module or a binary module depends on your needs. If you want a simple implementation, you can consider a binary module. If you want to be efficient, use a dynamic library module. In addition, if you need to speed up through parallel execution, you can also use binary modules. ```c++ [./modules/bar/bar.cpp] #include #include #include int main(int argc, char** argv) { int a = atoi(argv[1]); int b = atoi(argv[2]); printf("%d", a + b); return 0; } ``` ```lua [./modules/bar/xmake.lua] add_rules("mode.debug", "mode.release") target("add") -- Specify the target as a binary lua module add_rules("module.binary") add_files("bar.cpp") ``` ## Import native module For module import, we only need to call import, which is exactly the same as importing lua modules. ```lua [./xmake.lua] add_rules("mode.debug", "mode.release") --Add native modules in the ./modules directory add_moduledirs("modules") target("test") set_kind("phony") on_load(function(target) import("foo", {always_build = true}) import("bar") print("foo: 1 + 1 = %s", foo.add(1, 1)) print("foo: 1 - 1 = %s", foo.sub(1, 1)) print("bar: 1 + 1 = %s", bar.add(1, 1)) end) ``` Since the construction of the plug-in module is completely independent from the main project, the native module will only be built once. If you want to trigger incremental plug-in compilation, you need to configure `always_build = true`, so that xmake will detect it every time Check whether the plug-in code has been changed. If so, the plug-in will be automatically incrementally built. The first execution effect is as follows: ```sh ruki-2:native_module ruki$ xmake [50%]: cache compiling.release src/foo.c [50%]: cache compiling.release src/bar.c [75%]: linking.release libmodule_foo.dylib [75%]: linking.release module_bar [100%]: build ok, spent 1.296s foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` When executed for the second time, the plug-in will not be built and the module can be used directly: ```sh ruki-2:native_module ruki$ xmake foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` ## Use as codegen Through the new native module feature, we can also use it to implement auto-codegen, and then continue to execute the subsequent compilation process based on the automatically generated code. There is also a complete example here for reference: [autogen\_shared\_module](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/autogen/autogen_shared_module). --- --- url: /examples/embed/verilog.md --- ## iVerilog Simulator Through `add_requires("iverilog")` configuration, we can automatically pull the iverilog toolchain package, and then use `set_toolchains("@iverilog")` to automatically bind the toolchain to compile the project. ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") ``` ### Set abstract configuration ```Lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` We can use `set_languages("v1800-2009")` to set the language standard for switching Verilog. Currently supported values and mappings are as follows: ```lua ["v1364-1995"] = "-g1995" ["v1364-2001"] = "-g2001" ["v1364-2005"] = "-g2005" ["v1800-2005"] = "-g2005-sv" ["v1800-2009"] = "-g2009" ["v1800-2012"] = "-g2012" ``` ### Set custom flags ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") add_values("iverilogs.flags", "-DTEST") ``` ### Build the project ```sh $ xmake check iverilog... iverilog check vvp... vvp [50%]: linking.iverilog hello.vvp [100%]: build ok! ``` ### Run the program ```sh $ xmake run hello world! LXT2 INFO: dumpfile hello.vcd opened, ready for output. src/main.v:6: $finish called at 0 (1s) ``` More complete examples: [iVerilog Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/iverilog) ## Verilator Simulator Through `add_requires("verilator")` configuration, we can automatically pull the verilator toolchain package, and then use `set_toolchains("@verilator")` to automatically bind to the toolchain to compile the project. ```lua add_requires("verilator") target("Hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") ``` verilator project, we need an additional `sim_main.cpp` file to participate in the compilation, as the entry code of the program. ```c++ #include "hello.h" #include "verilated.h" (Simplified Chinese) int main(int argc, char** argv) { VerilatedContext* contextp = new VerilatedContext; contextp->commandArgs(argc, argv); hello* top = new hello{contextp}; while (!contextp->gotFinish()) { top->eval(); } remove top. Remove contextp. returns 0. } ``` ### Set abstract configuration ```lua add_requires("verilator") target("Hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` We can use `set_languages("v1800-2009")` to set the language standard for switching Verilog. Currently supported values and mappings are as follows. ```lua --Verilog ["v1364-1995"] = "+1364-1995ext+v". ["v1364-2001"] = "+1364-2001ext+v". ["v1364-2005"] = "+1364-2005ext+v". --system-Verilog ["v1800-2005"] = "+1800-2005ext+v". ["v1800-2009"] = "+1800-2009ext+v". ["v1800-2012"] = "+1800-2012ext+v", ["v1800-2017"] = "+1800-2017ext+v". ``` ### Set custom flags ```lua add_requires("verilator") target("Hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") add_values("verilator.flags", "--trace", "--timing") ``` ### Build the project ```sh $ xmake [ 0%]: compiling.verilog src/main.v [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello.cpp [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated_threads.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello__Syms.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h07139e86__0.cpp [15%]: cache compiling.release src/sim_main.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0.cpp [84%]: linking. release hello [100%]: build ok! ``` ### Run the program ```sh $ xmake run ruki-2:hello ruki$ xmake run hello world! - src/main.v:4:Verilog $finish ``` A more complete example: [Verilator](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/verilator) ### Compile static library We also provide `verilator.static` rules to compile and generate verilator static libraries. ```lua add_requires("verilator") target("hello") add_rules("verilator.static") set_toolchains("@verilator") add_files("src/*.v") target("test") add_deps("hello") add_files("src/*.cpp") ``` --- --- url: /zh/examples/embed/verilog.md --- ## iVerilog 仿真器 {#iverilog} 通过 `add_requires("iverilog")` 配置,我们能够自动拉取 iverilog 工具链包,然后使用 `set_toolchains("@iverilog")` 自动绑定工具链来编译工程。 ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") ``` ### 设置抽象配置 ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` 我们可以通过 `set_languages("v1800-2009")` 来设置切换 Verilog 的语言标准。 目前支持的一些取值和映射关系如下: ```lua ["v1364-1995"] = "-g1995" ["v1364-2001"] = "-g2001" ["v1364-2005"] = "-g2005" ["v1800-2005"] = "-g2005-sv" ["v1800-2009"] = "-g2009" ["v1800-2012"] = "-g2012" ``` ### 设置自定义 flags ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") add_values("iverilogs.flags", "-DTEST") ``` ### 构建工程 ```sh $ xmake checking for iverilog ... iverilog checking for vvp ... vvp [ 50%]: linking.iverilog hello.vvp [100%]: build ok! ``` ### 运行程序 ```sh $ xmake run hello world! LXT2 info: dumpfile hello.vcd opened for output. src/main.v:6: $finish called at 0 (1s) ``` 更多完整例子:[iVerilog Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/iverilog) ## Verilator 仿真器 {#verilator} 通过 `add_requires("verilator")` 配置,我们能够自动拉取 verilator 工具链包,然后使用 `set_toolchains("@verilator")` 自动绑定到工具链来编译工程。 ```lua add_requires("verilator") target("hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") ``` verilator 工程,我们需要一个额外的 `sim_main.cpp` 文件参与编译,作为程序的入口代码。 ```c++ #include "hello.h" #include "verilated.h" int main(int argc, char** argv) { VerilatedContext* contextp = new VerilatedContext; contextp->commandArgs(argc, argv); hello* top = new hello{contextp}; while (!contextp->gotFinish()) { top->eval(); } delete top; delete contextp; return 0; } ``` ### 设置抽象配置 ```lua add_requires("verilator") target("hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` 我们可以通过 `set_languages("v1800-2009")` 来设置切换 Verilog 的语言标准。 目前支持的一些取值和映射关系如下: ```lua -- Verilog ["v1364-1995"] = "+1364-1995ext+v", ["v1364-2001"] = "+1364-2001ext+v", ["v1364-2005"] = "+1364-2005ext+v", -- SystemVerilog ["v1800-2005"] = "+1800-2005ext+v", ["v1800-2009"] = "+1800-2009ext+v", ["v1800-2012"] = "+1800-2012ext+v", ["v1800-2017"] = "+1800-2017ext+v", ``` ### 设置自定义 flags ```lua add_requires("verilator") target("hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") add_values("verilator.flags", "--trace", "--timing") ``` ### 构建工程 ```sh $ xmake [ 0%]: compiling.verilog src/main.v [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello.cpp [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated_threads.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello__Syms.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h07139e86__0.cpp [ 15%]: cache compiling.release src/sim_main.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0.cpp [ 84%]: linking.release hello [100%]: build ok! ``` ### 运行程序 ```sh $ xmake run ruki-2:hello ruki$ xmake run hello world! - src/main.v:4: Verilog $finish ``` 更多完整例子:[Verilator](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/verilator) ### 编译静态库 我们也提供了 `verilator.static` 规则来编译生成 verilator 静态库。 ```lua add_requires("verilator") target("hello") add_rules("verilator.static") set_toolchains("@verilator") add_files("src/*.v") target("test") add_deps("hello") add_files("src/*.cpp") ``` --- --- url: /examples/bindings/python-module.md --- ## Swig We can use Swig to develop Python modules. For a detailed introduction, please see: [Python Modules with Swig](/examples/bindings/swig.html#python-c-module) ## Cython We can also use Cython to build Python modules. ```lua add_rules("mode.debug", "mode.release") add_requires("python 3.x") target("example") add_rules("python.cython") add_files("src/*.py") add_packages("python") ``` ```python [example.py] print("Hello, world!") ``` ## PyBind We can also use pybind11 to build python modules. ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.module") add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` ```c++ [example.cpp] #include #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(example, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif } ``` --- --- url: /zh/examples/bindings/python-module.md --- ## Swig 我们可以使用 Swig 来开发 Python 模块,详细的介绍可以看下:[Python Modules with Swig](/zh/examples/bindings/swig.html#python-c-module) ## Cython 我们也可以使用 Cython 来构建 Python 模块。 ```lua add_rules("mode.debug", "mode.release") add_requires("python 3.x") target("example") add_rules("python.cython") add_files("src/*.py") add_packages("python") ``` ```python [example.py] print("Hello, world!") ``` ## PyBind 我们还可以使用 pybind11 来构建 python 模块。 ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.module") add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` ```c++ [example.cpp] #include #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) int add(int i, int j) { return i + j; } namespace py = pybind11; PYBIND11_MODULE(example, m) { m.doc() = R"pbdoc( Pybind11 example plugin ----------------------- .. currentmodule:: example .. autosummary:: :toctree: _generate add subtract )pbdoc"; m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); m.def("subtract", [](int i, int j) { return i - j; }, R"pbdoc( Subtract two numbers Some other explanation about the subtract function. )pbdoc"); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif } ``` --- --- url: /api/scripts/extension-modules/net/http.md --- # net.http This module provides various operational support for http. The currently available interfaces are as follows: ## http.download * Download http file #### Function Prototype ::: tip API ```lua http.download(url: , outputfile: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Required. URL to download | | outputfile | Required. Output file path | | opt | Optional. Option parameters, supports the following:- `headers` - HTTP headers- `timeout` - Timeout duration- `useragent` - User agent | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage This interface is relatively simple, is simply download files. ```lua import("net.http") http.download("https://xmake.io", "/tmp/index.html") ``` --- --- url: /zh/api/scripts/extension-modules/net/http.md --- # net.http 此模块提供http的各种操作支持,目前提供的接口如下: ## http.download * 下载http文件 #### 函数原型 ::: tip API ```lua http.download(url: , outputfile: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 必需。要下载的URL地址 | | outputfile | 必需。输出文件路径 | | opt | 可选。选项参数,支持以下选项:- `headers` - HTTP头信息- `timeout` - 超时时间- `useragent` - 用户代理 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 下载成功返回 true,失败返回 false | #### 用法说明 这个接口比较简单,就是单纯的下载文件。 ```lua import("net.http") http.download("https://xmake.io", "/tmp/index.html") ``` --- --- url: /api/scripts/extension-modules/net/ping.md --- # net.ping This module provides host connectivity and latency testing capabilities. ::: tip TIP To use this module, you need to import it first: `import("net.ping")` ::: The `net.ping` module provides cross-platform host ping detection functionality. It automatically finds available tools on the system (prioritizing curl, then wget, finally ping) and uses appropriate parameters for detection. ## ping * Execute ping detection on a list of hosts #### Function Prototype ::: tip API ```lua ping(hosts, opt?) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | hosts | Required. Host address string or array of host addresses | | opt | Optional. Option parameter table, supports:- `force` - Whether to force refresh cache (default: false) | #### Return Value | Type | Description | |------|-------------| | table | Returns a mapping table from host addresses to latency times in milliseconds, returns 65535 on failure or timeout | #### Usage Ping a single host: ```lua import("net.ping") local results = ping("github.com") print("Latency:", results["github.com"], "ms") ``` Ping multiple hosts: ```lua import("net.ping") local results = ping({"github.com", "gitlab.com", "git.com"}) for host, time in pairs(results) do print(host, ":", time, "ms") end ``` Force refresh cache and re-detect: ```lua import("net.ping") -- Don't use cache, force re-detection local results = ping({"github.com", "gitlab.com"}, {force = true}) ``` The `net.ping` module supports multiple detection methods: 1. **Using curl**: If curl is installed on the system, it uses `curl` command for detection (returns milliseconds) 2. **Using wget**: If curl is not available, it tries to use `wget` command for detection (returns milliseconds) 3. **Using ping**: If both are unavailable, it uses the system's `ping` command for detection (different parameters for Windows/macOS/Linux) Detection results are cached, and subsequent requests use cached results directly unless `force = true` is set. Concurrent detection is supported for improved efficiency. Here is a complete example: ```lua import("net.ping") -- Test latency for multiple mirror sources local mirrors = { "github.com", "gitee.com", "code.csdn.net" } print("Testing mirror source latency...") local results = ping(mirrors) -- Find the mirror source with lowest latency local best_mirror = nil local min_time = math.maxinteger for host, time in pairs(results) do print(string.format("%s: %d ms", host, math.floor(time))) if time < min_time then min_time = time best_mirror = host end end if best_mirror then print(string.format("\nFastest mirror source: %s (latency: %d ms)", best_mirror, math.floor(min_time))) else print("No available mirror source found") end ``` --- --- url: /zh/api/scripts/extension-modules/net/ping.md --- # net.ping 此模块用于检测主机网络的连通性和延迟。 ::: tip 提示 使用此模块需要先导入:`import("net.ping")` ::: `net.ping` 模块提供了跨平台的主机 Ping 检测功能。它会自动查找系统可用的工具(优先 curl,然后 wget,最后 ping),并使用合适的参数进行检测。 ## ping * 对主机列表执行 Ping 检测 #### 函数原型 ::: tip API ```lua ping(hosts, opt?) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | hosts | 必需。主机地址字符串或主机地址数组 | | opt | 可选。选项参数表,支持:- `force` - 是否强制刷新缓存(默认为 false) | #### 返回值说明 | 类型 | 描述 | |------|------| | table | 返回主机地址到延迟时间的映射表,单位为毫秒,失败或无响应时返回 65535 | #### 用法说明 对单个主机进行 Ping 检测: ```lua import("net.ping") local results = ping("github.com") print("延迟:", results["github.com"], "ms") ``` 对多个主机进行 Ping 检测: ```lua import("net.ping") local results = ping({"github.com", "gitlab.com", "git.com"}) for host, time in pairs(results) do print(host, ":", time, "ms") end ``` 强制刷新缓存重新检测: ```lua import("net.ping") -- 不读取缓存,强制重新检测 local results = ping({"github.com", "gitlab.com"}, {force = true}) ``` `net.ping` 模块支持多种检测方式: 1. **使用 curl**:如果系统安装了 curl,会使用 `curl` 命令检测(返回值为毫秒) 2. **使用 wget**:如果未安装 curl,会尝试使用 `wget` 命令检测(返回值为毫秒) 3. **使用 ping**:如果前两者都不可用,会使用系统的 `ping` 命令检测(Windows/macOS/Linux 的参数不同) 检测结果会被缓存,后续请求会直接使用缓存结果,除非设置了 `force = true`。支持并发生检测以提高效率。以下是一个完整的示例: ```lua import("net.ping") -- 检测多个镜像源的延迟 local mirrors = { "github.com", "gitee.com", "code.csdn.net" } print("正在检测镜像源延迟...") local results = ping(mirrors) -- 找出延迟最低的镜像源 local best_mirror = nil local min_time = math.maxinteger for host, time in pairs(results) do print(string.format("%s: %d ms", host, math.floor(time))) if time < min_time then min_time = time best_mirror = host end end if best_mirror then print(string.format("\n最快镜像源: %s (延迟: %d ms)", best_mirror, math.floor(min_time))) else print("未找到可用镜像源") end ``` --- --- url: /guide/package-management/network-optimization.md --- # Network Optimization If the download package is slow or fails due to an unstable network, we can use the following methods to resolve it. ## Manual Download By default, xmake will call curl, wget, and other tools to download. Users can also manually download with their own downloader (you can also use a proxy), and put the downloaded package in their own directory, for example: `/download/packages/zlib-v1.0.tar.gz` Then use the following command to set the search directory for package downloads: ```sh $ xmake g --pkg_searchdirs="/download/packages" ``` Then re-execute xmake to compile. Xmake will first look for the source package from `/download/packages`, and then use it directly, no longer downloading it itself. As for the package name you are looking for, you can check it by the following command: ```sh $ xmake require --info zlib -> searchdirs: /download/packages -> searchnames: zlib-1.2.11.tar.gz ``` We can see the corresponding search directory and the searched package name. ## Use Proxy If manual downloading is still troublesome, we can also let xmake go directly to the proxy. ```sh $ xmake g --proxy="socks5://127.0.0.1:1086" $ xmake g --help -x PROXY, --proxy=PROXY Use proxy on given port. [PROTOCOL://]HOST[:PORT] e.g. -xmake g --proxy='http://host:port' -xmake g --proxy='https://host:port' -xmake g --proxy='socks5://host:port' ``` The `--proxy` parameter specifies the proxy protocol and address. The specific syntax can refer to curl. Usually, it can support http, https, socks5, and other protocols, but the actual support depends on curl, wget, and git. For example, wget does not support the socks5 protocol. We can use the following parameters to specify which hosts go to the proxy. If not set, the default is to go global. ```sh --proxy_hosts=PROXY_HOSTS Only enable proxy for the given hosts list, it will enable all if be unset, and we can pass match pattern to list: e.g. -xmake g --proxy_hosts='github.com,gitlab.*,*.xmake.io' ``` If the hosts list is set, then the matching hosts in this list will go to the proxy. `--proxy_host` supports multiple host settings, separated by commas, and supports basic pattern matching like \*.github.com, and other lua pattern matching rules are also supported. If we feel that the above hosts mode configuration is not flexible enough, we can also follow pac's automatic proxy configuration rules: ```sh --proxy_pac=PROXY_PAC Set the auto proxy configuration file. (default: pac.lua) e.g. -xmake g --proxy_pac=pac.lua (in /Users/ruki/.xmake or absolute path) -function main(url, host) if host =='github.com' then return true end end ``` ::: tip NOTE If there are proxy\_hosts, the host configuration is preferred, otherwise, the pac configuration can be used. ::: The default path of pac: ~/.xmake/pac.lua. If --proxy is set, and this file exists, it will automatically go to pac. If it does not exist, and there are no hosts, then the proxy will take effect globally. You can also manually specify the pac full path ```sh $ xmake g --proxy_pac=/xxxx/xxxxx_pac.lua ``` Configuration rule description: ```lua function main(url, host) if host:find("bintray.com") then return true end end ``` If it returns true, then the url and host are the proxy to go, not to return or return false, it is not to proxy. For specific details of this, see: https://github.com/xmake-io/xmake/issues/854 ## Configure Mirror Proxy After v2.5.4, mirror proxy rules can also be configured in the pac.lua configuration. For example, access to all github.com domain names is switched to the hub.fastgit.org domain name to achieve accelerated downloading of packages. ```lua function mirror(url) return url:gsub("github.com", "hub.fastgit.org") end ``` ```sh $ xrepo install libpng > curl https://hub.fastgit.org/glennrp/libpng/archive/v1.6.37.zip -o v1.6.37.zip ``` --- --- url: /posts/new-feature-announcement.md --- We're excited to announce a major enhancement to Xmake's package management system that will make dependency handling even more powerful and user-friendly. ## What's New ### Improved Dependency Resolution The new package management system features: * **Smart dependency resolution**: Automatically resolves complex dependency chains * **Version conflict detection**: Identifies and helps resolve version conflicts * **Parallel downloads**: Faster package installation with parallel downloading * **Better caching**: Improved caching system for faster subsequent builds ### Enhanced User Experience * **Simplified configuration**: Easier package declaration syntax * **Better error messages**: More informative error messages when things go wrong * **Progress indicators**: Visual feedback during package operations * **Offline support**: Better handling of offline scenarios ## Getting Started To use the new features, simply update your `xmake.lua` file: ```lua add_requires("boost", "openssl", "zlib") target("myapp") add_packages("boost", "openssl", "zlib") ``` The system will automatically handle all the complexity behind the scenes. ## Migration Guide Existing projects will continue to work without changes. The new features are backward compatible and can be adopted gradually. ## What's Next This is just the beginning. We have more exciting features planned for the upcoming releases: * Cloud package hosting * Package signing and verification * Advanced dependency graphs * Integration with more package repositories Stay tuned for more updates! --- --- url: /api/scripts/option-instance.md --- # Option Instance {#option-instance} This page describes the `option` interface for functions like `on_load()`, `on_check()` or `after_check()` of the [Configuration option](/api/description/configuration-option). In the script scope, you can operate various properties and configurations of the current option through the `option` parameter. ## option:name * Get the name of the option (without namespace) #### Function Prototype ::: tip API ```lua option:name() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("test") on_check(function (option) print(option:name()) -- output: test end) ``` ## option:fullname * Get the full name of the option (with namespace) #### Function Prototype ::: tip API ```lua option:fullname() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("mymod::test") on_check(function (option) print(option:fullname()) -- output: mymod::test end) ``` ## option:namespace * Get the namespace of the option #### Function Prototype ::: tip API ```lua option:namespace() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("mymod::test") on_check(function (option) print(option:namespace()) -- output: mymod end) ``` ## option:description * Get the description of the option #### Function Prototype ::: tip API ```lua option:description() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("test") set_description("This is a test option") on_check(function (option) print(option:description()) -- output: This is a test option end) ``` ## option:value * Get the current value of the option #### Function Prototype ::: tip API ```lua option:value() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("demo") set_default(true) after_check(function (option) local value = option:value() if value then print("demo option is enabled") else print("demo option is disabled") end end) ``` ## option:enabled * Check if the option is enabled #### Function Prototype ::: tip API ```lua option:enabled() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("test") after_check(function (option) if option:enabled() then print("Option is enabled") end end) ``` ## option:enable * Enable or disable the option #### Function Prototype ::: tip API ```lua option:enable(enable: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | enable | Whether to enable | #### Usage ```lua option("float") after_check(function (option) if option:dep("micro"):enabled() then -- If micro option is enabled, disable float option option:enable(false) end end) ``` ## option:set\_value * Set the value of the option #### Function Prototype ::: tip API ```lua option:set_value(value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | value | Option value | #### Usage ```lua option("test") on_check(function (option) -- Set option value to a specific value option:set_value("custom_value") end) ``` ## option:clear * Clear the option status, need to recheck #### Function Prototype ::: tip API ```lua option:clear() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("test") after_check(function (option) -- Clear status, will recheck on next build option:clear() end) ``` ## option:get * Get the configuration values of the option in the description scope #### Function Prototype ::: tip API ```lua option:get(key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | #### Usage Any configuration values set by `set_xxx` and `add_xxx` in the description scope can be obtained through this interface. ```lua option("test") set_default(false) set_category("option") set_description("Test option") add_defines("TEST_MODE") add_links("testlib") on_check(function (option) -- Get various configurations local default = option:get("default") -- false local category = option:get("category") -- "option" local description = option:get("description") -- "Test option" local defines = option:get("defines") -- {"TEST_MODE"} local links = option:get("links") -- {"testlib"} -- Get type checking related configurations local ctypes = option:get("ctypes") -- Get C type check list local cfuncs = option:get("cfuncs") -- Get C function check list local cincludes = option:get("cincludes") -- Get C header file check list end) ``` ## option:set * Set the configuration values of the option #### Function Prototype ::: tip API ```lua option:set(key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | | value | Configuration value | #### Usage If you want to add values, you can use [option:add](#option-add). ```lua option("test") on_check(function (option) -- Set link libraries option:set("links", "sdl2") -- Set predefined macros option:set("defines", "SDL_MAIN_HANDLED") -- Set configuration variables option:set("configvar", option:name(), option:value(), {quote = false}) -- Set compilation flags option:set("cxflags", "-O2", "-Wall") -- Set header file search paths option:set("includedirs", "/usr/include/sdl2") -- Set library file search paths option:set("linkdirs", "/usr/lib") end) ``` ::: tip NOTE Any script scope configuration using `option:set("xxx", ...)` is completely consistent with the corresponding `set_xxx` interface in the description scope. For specific parameter descriptions, you can directly refer to the corresponding `set_xxx` interface documentation in the description scope. For example: * Description scope: `set_default(false)` * Script scope: `option:set("default", false)` ::: ## option:add * Add values to the option by name #### Function Prototype ::: tip API ```lua option:add(key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | | value | Value to add | #### Usage ```lua option("test") on_check(function (option) -- Add link libraries option:add("links", "sdl2", "pthread") -- Add predefined macros option:add("defines", "DEBUG", "VERSION=1") -- Add compilation flags option:add("cxflags", "-g", "-O0") -- Add header file search paths option:add("includedirs", "/usr/local/include") -- Add library file search paths option:add("linkdirs", "/usr/local/lib") -- Add system link libraries option:add("syslinks", "dl", "m") -- Add C type checks option:add("ctypes", "wchar_t") -- Add C function checks option:add("cfuncs", "malloc", "free") -- Add C header file checks option:add("cincludes", "stdio.h", "stdlib.h") end) ``` ::: tip NOTE Any script scope configuration using `option:add("xxx", ...)` is completely consistent with the corresponding `add_xxx` interface in the description scope. For specific parameter descriptions, you can directly refer to the corresponding `add_xxx` interface documentation in the description scope. For example: * Description scope: `add_defines("DEBUG", "VERSION=1")` * Script scope: `option:add("defines", "DEBUG", "VERSION=1")` ::: ## option:remove * Remove specified values from the option #### Function Prototype ::: tip API ```lua option:remove(key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | | value | Value to remove | #### Usage ```lua option("test") on_check(function (option) -- Remove specific link libraries option:remove("links", "oldlib") -- Remove specific predefined macros option:remove("defines", "OLD_MACRO") -- Remove specific compilation flags option:remove("cxflags", "-Wall") end) ``` ## option:deps * Get all dependencies of the option #### Function Prototype ::: tip API ```lua option:deps() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("info") add_deps("small", "micro") after_check(function (option) local deps = option:deps() if deps then for name, dep in pairs(deps) do print("Dependency:", name, "enabled:", dep:enabled()) end end end) ``` ## option:dep * Get the specified dependent option #### Function Prototype ::: tip API ```lua option:dep(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Dependency name | #### Usage ```lua option("float") add_deps("micro") after_check(function (option) local micro_dep = option:dep("micro") if micro_dep and micro_dep:enabled() then -- If micro dependency is enabled, disable current option option:enable(false) end end) ``` ## option:orderdeps * Get the ordered dependencies of the option #### Function Prototype ::: tip API ```lua option:orderdeps() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua option("test") add_deps("dep1", "dep2") after_check(function (option) local orderdeps = option:orderdeps() if orderdeps then for i, dep in ipairs(orderdeps) do print("Order dependency", i, ":", dep:name()) end end end) ``` ## option:extraconf * Get extra configuration information #### Function Prototype ::: tip API ```lua option:extraconf(name: , key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration name | | key | Configuration key | #### Usage ```lua option("test") add_csnippets("test_snippet", "int test() { return 1; }", {output = true}) on_check(function (option) -- Check if snippet has output configuration local has_output = option:extraconf("csnippets", "test_snippet", "output") if has_output then print("test_snippet has output configuration") end end) ``` ::: tip TIP * In script functions like `on_check`, `after_check`, the `option` parameter represents the current option instance being processed * You can get various configurations set in the description scope through `option:get()` * You can dynamically modify option configurations through `option:set()` and `option:add()` * Use `option:dep()` to access dependent options and implement complex option logic * The enable/disable status of options can be controlled through `option:enabled()` and `option:enable()` ::: --- --- url: /api/scripts/builtin-modules/os.md --- # os The system operation module belongs to the built-in module. It can be called directly by the script scope without using [import](/api/scripts/builtin-modules/import) to import. This module is also a native module of lua, and xmake has been extended to provide more practical interfaces. ::: tip NOTE Only some readonly interfaces (for example: `os.getenv`, `os.arch`) in the os module can be used in the description scope. Other interfaces can only be used in the script domain, for example: `os.cp`, `os .rm`etc. ::: ## os.cp * Copy files or directories #### Function Prototype ::: tip API ```lua os.cp(source: , destination: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | source | Source path or pattern | | destination | Destination path | | options | Options table (optional) | #### Usage 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", "$(builddir)/inc") os.cp("$(projectdir)/src/test/**.h", "$(builddir)/inc") ``` The above code will copy all the header files in the current `xmake.lua` directory, the header files in the project source test directory to the `$(builddir)` output directory. Among them `$(scriptdir)`, `$(projectdir)` These variables are built-in variables of xmake. For details, see the related documentation of [built-in variables](/api/description/builtin-variables). The matching patterns in `*.h` and `**.h` are similar to those in [add\_files](/api/description/project-target#add-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") ``` The copy at the top will expand and copy all files to the specified directory, and lose the source directory hierarchy. If you want to copy according to the directory structure that maintains it, you can set the rootdir parameter: ```lua os.cp ("src/**.h", "/tmp/", {rootdir="src"}) ``` The above script can press the root directory of `src` to copy all sub-files under src in the same directory structure. ::: tip NOTE Try to use the `os.cp` interface instead of `os.run("cp ..")`, which will ensure platform consistency and cross-platform build description. ::: Under 2.5.7, the parameter `{symlink = true}` is added to keep the symbolic link when copying files. ```lua os.cp("/xxx/foo", "/xxx/bar", {symlink = true}) ``` Since v3.0.4, the parameter `{copy_if_different = true}` is added to copy files only when the source and destination file contents differ. If the file contents are the same, the copy operation will be skipped, preserving the destination file's metadata such as mtime. This helps avoid unnecessary incremental builds. ```lua os.cp("$(scriptdir)/config.h", "$(builddir)/inc/config.h", {copy_if_different = true}) ``` ## os.mv * Move to rename a file or directory #### Function Prototype ::: tip API ```lua os.mv(source: , destination: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | source | Source path or pattern | | destination | Destination path | #### Usage 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("$(builddir)/test1", "$(tmpdir)") -- File movement does not support bulk operations, which is file renaming os.mv("$(builddir)/libtest.a", "$(builddir)/libdemo.a") ``` ## os.rm * Delete files or directory trees #### Function Prototype ::: tip API ```lua os.rm(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | File or directory path | #### Usage Support for recursive deletion of directories, bulk delete operations, and pattern matching and built-in variables, such as: ```lua os.rm("$(builddir)/inc/**.h") os.rm("$(builddir)/lib/") ``` ## os.trycp * Try copying files or directories #### Function Prototype ::: tip API ```lua os.trycp(source: , destination: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | source | Source path or pattern | | destination | Destination path | #### Usage 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 #### Function Prototype ::: tip API ```lua os.trymv(source: , destination: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | source | Source path or pattern | | destination | Destination path | #### Usage 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 #### Function Prototype ::: tip API ```lua os.tryrm(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | File or directory path | #### Usage 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 #### Function Prototype ::: tip API ```lua os.cd(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Directory path | #### Usage 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 #### Function Prototype ::: tip API ```lua os.rmdir(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Directory path | #### Usage If it is not a directory, it cannot be deleted. ## os.mkdir * Create a directory #### Function Prototype ::: tip API ```lua os.mkdir(path: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Directory path | | ... | Variable arguments, can pass multiple directory paths | #### Usage Support for batch creation and built-in variables, such as: ```lua os.mkdir("$(tmpdir)/test", "$(builddir)/inc") ``` Supports recursive creation of multi-level directories, automatically creating parent directories if they don't exist. ## os.touch * Create an empty file or update file timestamp #### Function Prototype ::: tip API ```lua os.touch(path: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | File path | | ... | Variable arguments, can pass multiple file paths | #### Usage If the file doesn't exist, creates an empty file. If the file already exists, updates the file's modification time to the current time. Supports batch creation: ```lua os.touch("file1.txt", "file2.txt", "file3.txt") ``` ## os.isdir * Determine if it is a directory #### Function Prototype ::: tip API ```lua os.isdir(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Directory path | #### Usage Return false if the directory does not exist ```lua if os.isdir("src") then -- ... end ``` ## os.isfile * Determine if it is a file #### Function Prototype ::: tip API ```lua os.isfile(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | File path | #### Usage Return false if the file does not exist ```lua if os.isfile("$(builddir)/libxxx.a") then -- ... end ``` ## os.exists * Determine if a file or directory exists #### Function Prototype ::: tip API ```lua os.exists(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | File or directory path | #### Usage Return false if the file or directory does not exist ```lua -- Judging the existence of the directory if os.exists("$(builddir)") then -- ... end -- Judging the existence of the file if os.exists("$(builddir)/libxxx.a") then -- ... end ``` ## os.islink * Determine if it is a symbolic link #### Function Prototype ::: tip API ```lua os.islink(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Symbolic link path | #### Usage Determines whether the specified path is a symbolic link. Returns false if it is not a symbolic link or doesn't exist. ```lua if os.islink("path/to/symlink") then -- It is a symbolic link local target = os.readlink("path/to/symlink") print("Link target:", target) end ``` Used with [os.ln](#os-ln): ```lua os.ln("source.txt", "link.txt") assert(os.islink("link.txt")) ``` ## os.dirs * Traverse to get all the directories under the specified directory #### Function Prototype ::: tip API ```lua os.dirs(pattern: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | pattern | File pattern | #### Usage 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("$(builddir)/inc/**")) do print(dir) end ``` ## os.files * Traverse to get all the files in the specified directory #### Function Prototype ::: tip API ```lua os.files(pattern: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | pattern | File pattern | #### Usage 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("$(builddir)/inc/*.h")) do print(filepath) end ``` ## os.filedirs * Traverse to get all files and directories under the specified directory #### Function Prototype ::: tip API ```lua os.filedirs(pattern: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | pattern | File pattern | #### Usage 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("$(builddir)/**")) do print(filedir) end ``` ## os.exit * Exit the program #### Function Prototype ::: tip API ```lua os.exit(code: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | code | Exit code | #### Usage Exits the current program and returns the specified exit code. If no exit code is specified, defaults to 0 (success). ```lua -- Normal exit os.exit(0) -- Exit with error if error_occurred then os.exit(1) end ``` ## os.isexec * Test if a file is executable #### Function Prototype ::: tip API ```lua os.isexec(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | File path | #### Usage Determines whether the specified file has executable permissions. On Unix systems, it checks the file's execute permission bits; on Windows, it checks the file extension. Used for dynamically detecting executable files: ```lua local program = "/usr/bin/gcc" if os.isexec(program) then print("Program is executable") os.execv(program, {"--version"}) else print("Program is not executable or doesn't exist") end ``` ## os.run * Quietly running native shell commands #### Function Prototype ::: tip API ```lua os.run(command: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | command | Command string | | ... | Variable arguments for command | #### Usage 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 $(builddir)") ``` ::: tip WARN 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. ::: For more advanced process operations and control, see the [process](#process) module interface. ## os.runv * Quietly running native shell commands with parameter list #### Function Prototype ::: tip API ```lua os.runv(program: , args:
, options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | program | Program name | | args | Arguments table | | options | Options table (optional) | #### Usage Similar to [os.run](#os-run), just the way to pass parameters is passed through the parameter list, not the string command, for example: ```lua os.runv("echo", {"hello", "xmake!"}) ``` ## os.exec * Echo running native shell commands #### Function Prototype ::: tip API ```lua os.exec(command: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | command | Command string | | ... | Variable arguments for command | #### Usage Similar to the [os.run](#os-run) interface, the only difference is that when this interface executes the shell program, it has the output output, which is used in general debugging. ## os.execv * Echo running native shell commands with parameter list #### Function Prototype ::: tip API ```lua os.execv(program: , args:
, options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | program | Program name | | args | Arguments table | | options | Options table (optional) | #### Usage Similar to [os.exec](#os-exec), just the way to pass parameters is passed through the parameter list, not the string command, for example: ```lua os.execv("echo", {"hello", "xmake!"}) ``` In addition, this interface also supports an optional parameter for passing settings: redirect output, perform environment variable settings, for example: ```lua os.execv("echo", {"hello", "xmake!"}, {stdout = outfile, stderr = errfile, envs = {PATH = "xxx;xx", CFLAGS = "xx"}} ``` The stdout and stderr parameters are used to pass redirected output and error output. You can directly pass in the file path or the file object opened by io.open. After v2.5.1, we also support setting the stdin parameter to support redirecting input files. ::: tip NOTE stdout/stderr/stdin can simultaneously support three types of values: file path, file object, and pipe object. ::: ### Redirecting to Files ```lua -- Redirect output to file os.execv("echo", {"hello"}, {stdout = "output.txt"}) -- Using file object local outfile = io.open("output.txt", "w") os.execv("echo", {"hello"}, {stdout = outfile}) outfile:close() ``` ### Redirecting to Pipes Combined with the pipe module, you can capture subprocess output for processing: ```lua import("core.base.pipe") import("core.base.bytes") -- Create pipe local rpipe, wpipe = pipe.openpair() -- Redirect subprocess stdout to pipe os.execv("ls", {"-l"}, {stdout = wpipe}) -- Close write end, read output wpipe:close() local buff = bytes(8192) local read, data = rpipe:read(buff, 8192) if read > 0 then print("Command output:", data:str()) end rpipe:close() ``` Redirecting both stdout and stderr simultaneously: ```lua import("core.base.pipe") import("core.base.bytes") local rpipe_out, wpipe_out = pipe.openpair() local rpipe_err, wpipe_err = pipe.openpair() -- Redirect stdout and stderr separately os.execv("make", {}, {stdout = wpipe_out, stderr = wpipe_err}) wpipe_out:close() wpipe_err:close() -- Read stdout local buff = bytes(8192) local read, output = rpipe_out:read(buff, 8192) print("Stdout:", output and output:str() or "") -- Read stderr local read, errors = rpipe_err:read(buff, 8192) print("Stderr:", errors and errors:str() or "") rpipe_out:close() rpipe_err:close() ``` In addition, if you want to temporarily set and rewrite some environment variables during this execution, you can pass the envs parameter. The environment variable settings inside will replace the existing settings, but will not affect the outer execution environment, only the current command. We can also get all the current environment variables through the `os.getenvs()` interface, and then pass in the envs parameter after rewriting some parts. ## os.iorun * Quietly running native shell commands and getting output #### Function Prototype ::: tip API ```lua os.iorun(command: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | command | Command string | | ... | Variable arguments for command | #### Usage Similar to the [os.run](#os-run) interface, the only difference is that after executing the shell program, this interface will get the execution result of the shell program, which is equivalent to redirecting the output. You can get the contents of `stdout`, `stderr` at the same time, for example: ```lua local outdata, errdata = os.iorun("echo hello xmake!") ``` ## os.iorunv * Run the native shell command quietly and get the output with a list of parameters #### Function Prototype ::: tip API ```lua os.iorunv(program: , args:
, options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | program | Program name | | args | Arguments table | | options | Options table (optional) | #### Usage Similar to [os.iorun](#os-iorun), just the way to pass arguments is passed through the argument list, not the string command, for example: ```lua local outdata, errdata = os.iorunv("echo", {"hello", "xmake!"}) local outdata, errdata = os.iorunv("echo", {"hello", "xmake!"}, {envs = {PATH="..."}}) ``` ## os.tmpdir * Get temporary directory #### Function Prototype ::: tip API ```lua os.tmpdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Consistent with the result of [$(tmpdir)](/api/description/builtin-variables#var-tmpdir), it is just a direct return to return a variable that can be maintained with subsequent strings. ```lua print(path.join(os.tmpdir(), "file.txt")) ``` Equivalent to: ```lua print("$(tmpdir)/file.txt") ``` ## os.tmpfile * Get temporary file path #### Function Prototype ::: tip API ```lua os.tmpfile() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Generates a unique temporary file path, returns only the path string, the file itself is not automatically created and needs to be created manually. Each call generates a different temporary file path, suitable for creating temporary files: ```lua -- Generate temporary file path local tmpfile = os.tmpfile() print("Temp file:", tmpfile) -- e.g.: /tmp/xmake_XXXXXX -- Create and use temporary file io.writefile(tmpfile, "temporary data") -- Delete after use os.rm(tmpfile) ``` ## os.curdir * Get the current directory path Consistent with the result of [$(curdir)](/api/description/builtin-variables#var-curdir), it is just a direct return to return a variable that can be maintained with subsequent strings. Usage reference: [os.tmpdir](#os-tmpdir). ## os.filesize * Get file size #### Function Prototype ::: tip API ```lua os.filesize(filepath: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filepath | File path | #### Usage Returns the size of the file in bytes. Returns 0 if the file doesn't exist or is inaccessible. Practical examples: ```lua local size = os.filesize("build/output.bin") if size > 0 then print(string.format("File size: %.2f KB", size / 1024)) end -- Check if file is empty if os.filesize("config.txt") == 0 then print("Config file is empty") end ``` ## os.scriptdir * Get the path of the current description script #### Function Prototype ::: tip API ```lua os.scriptdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Consistent with the result of [$(scriptdir)](/api/description/builtin-variables#var-scriptdir), it is just a direct return to return a variable that can be maintained with subsequent strings. Usage reference: [os.tmpdir](#os-tmpdir). ## os.programdir * Get the xmake installation main program script directory #### Function Prototype ::: tip API ```lua os.programdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Consistent with the result of [$(programdir)](/api/description/builtin-variables#var-programdir), it is just a direct get returned to a variable, which can be maintained with subsequent strings. ## os.programfile * Get the path of the xmake executable #### Function Prototype ::: tip API ```lua os.programfile() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the full path to the xmake executable. ```lua print("xmake path:", os.programfile()) -- e.g.: /usr/local/bin/xmake ``` ## os.projectdir * Get the project home directory Consistent with the result of [$(projectdir)](/api/description/builtin-variables#var-projectdir), it is just a direct return to return a variable that can be maintained with subsequent strings. ## os.arch * Get current system architecture #### Function Prototype ::: tip API ```lua os.arch() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the default architecture of the current host system. For example, executing xmake on `linux x86_64` returns: `x86_64` Common architecture values: `x86_64`, `i386`, `arm64`, `armv7`, `mips`, etc. ```lua print("Current architecture:", os.arch()) -- Execute different operations based on architecture if os.arch() == "x86_64" then add_defines("ARCH_X64") end ``` ## os.host * Get the operating system of the current host #### Function Prototype ::: tip API ```lua os.host() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Consistent with the result of [$(host)](/api/description/builtin-variables#var-host). For example, executing xmake on `linux x86_64` returns: `linux` Common system values: `linux`, `macosx`, `windows`, `bsd`, etc. ```lua print("Current system:", os.host()) -- Execute different operations based on system if os.host() == "windows" then add_defines("WINDOWS") elseif os.host() == "linux" then add_defines("LINUX") end ``` ## os.subhost * Get Subsystem host #### Function Prototype ::: tip API ```lua os.subhost() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Gets the current subsystem environment, such as msys or cygwin on Windows. If not running in a subsystem environment, returns the same value as [os.host()](#os-host). ```lua -- In MSYS2 environment print(os.subhost()) -- Returns: msys -- Detect if running in subsystem if os.subhost() ~= os.host() then print("Running in subsystem environment") end ``` ## os.subarch * Get Subsystem host architecture ```lua local subarch = os.subarch() ``` Gets the architecture of the subsystem. If not running in a subsystem environment, returns the same value as [os.arch()](#os-arch). ## os.is\_host * Test if a given host is the current #### Function Prototype ::: tip API ```lua os.is_host(host: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | host | Host name | | ... | Variable arguments, can pass multiple hosts | #### Usage ```lua if os.is_host("linux") then -- On Linux system end if os.is_host("macosx", "linux") then -- On macOS or Linux system end ``` Supports checking multiple systems at once, returns true if any matches. ::: tip TIP It's recommended to use the more concise built-in interface `is_host()` without the `os.` prefix, with the same usage: ```lua if is_host("linux") then -- On Linux system end ``` ::: ## os.is\_arch * Test if a given arch is the current #### Function Prototype ::: tip API ```lua os.is_arch(arch: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | arch | Architecture name | | ... | Variable arguments, can pass multiple architectures | #### Usage ```lua if os.is_arch("x86_64") then -- On x86_64 architecture end if os.is_arch("x86_64", "arm64") then -- On x86_64 or arm64 architecture end ``` Supports checking multiple architectures at once. ## os.is\_subhost * Test if a given sub host is the current #### Function Prototype ::: tip API ```lua os.is_subhost(subhost: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | subhost | Sub host name | | ... | Variable arguments, can pass multiple sub hosts | #### Usage ```lua if os.is_subhost("msys") then -- In MSYS subsystem end ``` Used to detect if running in a specific subsystem environment, such as msys or cygwin. ::: tip TIP It's recommended to use the more concise built-in interface `is_subhost()` with the same usage. ::: ## os.is\_subarch * Test if a given sub arch is the current #### Function Prototype ::: tip API ```lua os.is_subarch(subarch: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | subarch | Sub architecture name | | ... | Variable arguments, can pass multiple sub architectures | #### Usage ```lua if os.is_subarch("x86_64") then -- Subsystem architecture is x86_64 end ``` ## os.ln * Create a symlink to a file or directory #### Function Prototype ::: tip API ```lua os.ln(source: , target: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | source | Source file or directory path | | target | Target symlink path | #### Usage ```lua -- creates a symlink file "xxx.txt.ln" which is pointing to "xxx.txt" os.ln("xxx.txt", "xxx.txt.ln") ``` ## os.readlink * Read the content of a symlink #### Function Prototype ::: tip API ```lua os.readlink(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Symlink path | #### Usage Reads the target path that the symbolic link points to. Returns nil if the specified path is not a symbolic link. Used with [os.ln](#os-ln) and [os.islink](#os-islink): ```lua os.ln("source.txt", "link.txt") if os.islink("link.txt") then local target = os.readlink("link.txt") print("Link points to:", target) -- Output: source.txt end ``` ## os.raise * Raise an exception and abort the current script #### Function Prototype ::: tip API ```lua os.raise(message: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | message | Error message | #### Usage ```lua -- Raise exception with message "an error occurred" os.raise("an error occurred") ``` ::: tip NOTE Recommanded to use builtin function `raise` instead of `os.raise` ::: ## os.raiselevel * Similar to [os.raise](#os-raise) but you can specify the level of the error #### Function Prototype ::: tip API ```lua os.raiselevel(level: , message: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | level | Error level | | message | Error message | #### Usage ```lua -- Raise exception with message "an error occurred" os.raiselevel(3,"an error occurred") ``` ## os.features * Get features #### Function Prototype ::: tip API ```lua os.features() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Gets a list of features supported by the current operating system. Returns a table containing various system-supported features. ## os.getenvs * Get all current environment variables #### Function Prototype ::: tip API ```lua os.getenvs() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua local envs = os.getenvs() --- home directory (on linux) print(envs["HOME"]) ``` ## os.setenvs * Set environment variables. Replace the current envs by a new one and return old envs #### Function Prototype ::: tip API ```lua os.setenvs(envs:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | envs | Environment variables table | #### Usage ## os.addenvs * Add environment variables to current envs, return the all old envs #### Function Prototype ::: tip API ```lua os.addenvs(envs:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | envs | Environment variables table to add | #### Usage ```lua os.setenvs({EXAMPLE = "a/path"}) -- add a custom variable to see addenvs impact on it local oldenvs = os.addenvs({EXAMPLE = "some/path/"}) print(os.getenvs()["EXAMPLE"]) --got some/path/;a/path print(oldenvs["EXAMPLE"]) -- got a/path ``` ## os.joinenvs * Join environment variables. Similar to [os.addenvs](#os-addenvs) but with two envs variable #### Function Prototype ::: tip API ```lua os.joinenvs(envs1:
, envs2:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | envs1 | First environment variables table | | envs2 | Second environment variables table | #### Usage ```lua local envs = {CUSTOM = "a/path"} local envs2 = {CUSTOM = "some/path/"} print(os.joinenvs(envs, envs2)) ``` The result is: `{ CUSTOM = "a/path;some/path/" }` ## os.getenv * Get system environment variables #### Function Prototype ::: tip API ```lua os.getenv(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Environment variable name | #### Usage Gets the value of the specified environment variable. Returns nil if the environment variable doesn't exist. ```lua local path = os.getenv("PATH") if path then print("PATH:", path) end -- Get environment variable with default value local home = os.getenv("HOME") or "/tmp" ``` ## os.setenv * Set system environment variables #### Function Prototype ::: tip API ```lua os.setenv(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Environment variable name | | value | Environment variable value | #### Usage Sets the value of the specified environment variable. After setting, it affects the current process and its child processes. ```lua -- Set environment variable os.setenv("MY_VAR", "my_value") print(os.getenv("MY_VAR")) -- Output: my_value -- Set PATH os.setenv("PATH", "/new/path:" .. os.getenv("PATH")) ``` ## os.addenv * Add values to one environment variable #### Function Prototype ::: tip API ```lua os.addenv(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Environment variable name | | value | Value to add | #### Usage Appends a new value to the specified environment variable, using the system default separator (`:` on Unix, `;` on Windows). ```lua -- Add new path to PATH os.addenv("PATH", "/usr/local/bin") -- Verify print(os.getenv("PATH")) -- New path will be appended to existing PATH ``` ## os.setenvp * Setting environment variables with a given separator #### Function Prototype ::: tip API ```lua os.setenvp(name: , value: , separator: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Environment variable name | | value | Environment variable value | | separator | Separator string | #### Usage Sets an environment variable using a specified separator. Similar to [os.setenv](#os-setenv), but allows custom separator. ## os.addenvp * Add values to one environment variable with a given separator #### Function Prototype ::: tip API ```lua os.addenvp(name: , value: , separator: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Environment variable name | | value | Value to add | | separator | Separator string | #### Usage Appends a value to an environment variable using a specified separator. Similar to [os.addenv](#os-addenv), but allows custom separator. ## os.workingdir * Get the working directory #### Function Prototype ::: tip API ```lua os.workingdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua local workdir = os.workingdir() ``` Gets the absolute path of the current working directory. Similar to `os.curdir()`, but returns the working directory instead of the current script execution directory. ```lua print("Working directory:", os.workingdir()) ``` ## os.isroot * Test if xmake is running as root #### Function Prototype ::: tip API ```lua os.isroot() ``` ::: #### Parameter Description No parameters required for this function. #### Usage On Unix systems, checks if running as root user; on Windows, checks if running with administrator privileges. Useful when certain operations require administrator privileges: ```lua if not os.isroot() then raise("This operation requires administrator privileges, please use sudo or run as administrator") end ``` ## os.fscase * Test if the os has a case sensitive filesystem #### Function Prototype ::: tip API ```lua os.fscase() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns true if the filesystem is case-sensitive (like Linux), false if not (like Windows, macOS default). Useful for handling cross-platform filename compatibility: ```lua if not os.fscase() then -- On case-insensitive systems, avoid using filenames that differ only in case print("Warning: Filesystem is case-insensitive") end ``` ## os.term * Get current terminal (windows-terminal, vscode, ... ) #### Function Prototype ::: tip API ```lua os.term() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua print(os.term()) -- got vscode ``` ## os.shell * Get current shell (pwsh, cmd, ...) #### Function Prototype ::: tip API ```lua os.shell() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua print(os.shell()) -- got pwsh ``` ## os.cpuinfo * Get cpu information #### Function Prototype ::: tip API ```lua os.cpuinfo(key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | CPU info key (optional) | #### Usage ```lua print(os.cpuinfo()) -- got { -- ncpu = 8, -- usagerate = 0.0, -- model_name = "Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz", -- march = "Kaby Lake", -- vendor = "GenuineIntel", -- model = 158, -- family = 6 -- } print(os.cpuinfo("march")) -- got "Kaby Lake" ``` ## os.meminfo * Get memory information #### Function Prototype ::: tip API ```lua os.meminfo(key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Memory info key (optional) | #### Usage ```lua print(os.meminfo()) -- got { -- usagerate = 0.53490080822924, -- totalsize = 16332, -- availsize = 7596, -- pagesize = 4096 -- } ``` ## os.default\_njob * Get default parallel jobs #### Function Prototype ::: tip API ```lua os.default_njob() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the default number of parallel compilation jobs, typically equal to the number of CPU cores. ```lua local njob = os.default_njob() print("Default parallel jobs:", njob) ``` ## os.argv * Parse command line string into argument list #### Function Prototype ::: tip API ```lua os.argv(command: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | command | Command line string | | options | Options table (optional) | #### Usage Parses a command line string into an argument array, supporting quotes, escape characters, and other complex formats. Parsing rules: * Supports double quotes and single quotes for wrapping arguments * Supports escape characters (`\`) * Automatically handles space separation * Handles special characters like parentheses, backslashes, etc. Examples: ```lua -- Simple arguments os.argv("aa bb cc") -- Returns: {"aa", "bb", "cc"} -- Arguments with quotes os.argv('"aa bb cc" dd') -- Returns: {"aa bb cc", "dd"} -- Arguments with equals os.argv("--bb=bbb -c") -- Returns: {"--bb=bbb", "-c"} -- Escaped quotes os.argv('-DTEST=\\"hello\\"') -- Returns: {'-DTEST="hello"'} -- Complex arguments os.argv('-DTEST="hello world"') -- Returns: {'-DTEST=hello world'} ``` Supports `splitonly` option to only split without processing quotes: ```lua os.argv('-DTEST="hello world"', {splitonly = true}) -- Returns: {'-DTEST="hello world"'} ``` ## os.args * Convert argument list to command line string #### Function Prototype ::: tip API ```lua os.args(args:
, options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | args | Arguments array | | options | Options table (optional) | #### Usage Converts an argument array to a command line string, the inverse operation of [os.argv](#os-argv). Automatically handles special characters: * Arguments containing spaces are automatically quoted * Automatically escapes special characters * Handles backslashes in paths Examples: ```lua -- Simple arguments os.args({"aa", "bb", "cc"}) -- Returns: "aa bb cc" -- Arguments with spaces os.args({"aa bb cc", "dd"}) -- Returns: '"aa bb cc" dd' -- Arguments with quotes os.args({'-DTEST="hello"'}) -- Returns: '-DTEST=\\"hello\\"' -- Path arguments os.args({"aa\\bb/cc dd", "ee"}) -- Returns: '"aa\\\\bb/cc dd" ee' ``` Supports `escape` option to enable additional escaping: ```lua os.args({"aa\\bb/cc", "dd"}, {escape = true}) -- Returns: "aa\\\\bb/cc dd" ``` Round-trip conversion with `os.argv`: ```lua local cmdline = "gcc -o test test.c" local args = os.argv(cmdline) local cmdline2 = os.args(args) -- cmdline2 should be equivalent to cmdline ``` ## os.mclock * Get monotonic clock time (milliseconds) #### Function Prototype ::: tip API ```lua os.mclock() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua local start = os.mclock() -- Perform some operations local elapsed = os.mclock() - start print("Elapsed:", elapsed, "ms") ``` Returns a monotonically increasing timestamp (milliseconds), suitable for measuring time intervals. Unlike `os.clock()`, `os.mclock()` returns a monotonic clock that is not affected by system time adjustments, making it more suitable for performance measurement: ```lua local function benchmark(func) local start = os.mclock() func() local elapsed = os.mclock() - start print(string.format("Execution time: %.2f ms", elapsed)) end benchmark(function() os.sleep(100) end) ``` --- --- url: /zh/api/scripts/builtin-modules/os.md --- # os 系统操作模块,属于内置模块,无需使用[import](/zh/api/scripts/builtin-modules/import)导入,可直接脚本域调用其接口。 此模块也是lua的原生模块,xmake在其基础上进行了扩展,提供更多实用的接口。 ::: tip 注意 os 模块里面只有部分readonly接口(例如:`os.getenv`, `os.arch`)是可以在描述域中使用,其他接口只能在脚本域中使用,例如:`os.cp`, `os.rm`等 ::: ## os.cp * 复制文件或目录 #### 函数原型 ::: tip API ```lua os.cp(source: , destination: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | source | 源路径或模式 | | destination | 目标路径 | | options | 选项表(可选) | #### 用法说明 行为和shell中的`cp`命令类似,支持路径通配符匹配(使用的是lua模式匹配),支持多文件复制,以及内置变量支持。 例如: ```lua os.cp("$(scriptdir)/*.h", "$(builddir)/inc") os.cp("$(projectdir)/src/test/**.h", "$(builddir)/inc") ``` 上面的代码将:当前`xmake.lua`目录下的所有头文件、工程源码test目录下的头文件全部复制到`$(builddir)`输出目录中。 其中`$(scriptdir)`, `$(projectdir)` 这些变量是xmake的内置变量,具体详情见:[内置变量](/zh/api/description/builtin-variables)的相关文档。 而`*.h`和`**.h`中的匹配模式,跟[add\_files](/zh/api/description/project-target#add-files)中的类似,前者是单级目录匹配,后者是递归多级目录匹配。 此接口同时支持目录的`递归复制`,例如: ```lua -- 递归复制当前目录到临时目录 os.cp("$(curdir)/test/", "$(tmpdir)/test") ``` 上面的复制,会把所有文件全部展开复制到指定目录,丢失源目录层级,如果要按保持原有的目录结构复制,可以设置rootdir参数: ```lua os.cp("src/**.h", "/tmp/", {rootdir = "src"}) ``` 上面的脚本可以按`src`根目录,将src下的所有子文件保持目录结构复制过去。 ::: tip 注意 尽量使用`os.cp`接口,而不是`os.run("cp ..")`,这样更能保证平台一致性,实现跨平台构建描述。 ::: 2.5.7 下,新增 `{symlink = true}` 参数,在复制文件时候保留符号链接。 ```lua os.cp("/xxx/foo", "/xxx/bar", {symlink = true}) ``` v3.0.4 以上版本,新增 `{copy_if_different = true}` 参数,仅在源文件和目标文件内容不同时才执行复制操作。如果文件内容相同,则不会重新复制,这样可以保持目标文件的 mtime 等元数据不变,避免不必要的增量构建。 ```lua os.cp("$(scriptdir)/config.h", "$(builddir)/inc/config.h", {copy_if_different = true}) ``` ## os.mv * 移动重命名文件或目录 #### 函数原型 ::: tip API ```lua os.mv(source: , destination: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | source | 源路径或模式 | | destination | 目标路径 | #### 用法说明 跟[os.cp](#os-cp)的使用类似,同样支持多文件移动操作和模式匹配,例如: ```lua -- 移动文件到临时目录 os.mv("$(builddir)/test1", "$(tmpdir)") -- 文件移动不支持批量操作,也就是文件重命名 os.mv("$(builddir)/libtest.a", "$(builddir)/libdemo.a") ``` ## os.rm * 删除文件或目录树 #### 函数原型 ::: tip API ```lua os.rm(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 文件或目录路径 | #### 用法说明 支持递归删除目录,批量删除操作,以及模式匹配和内置变量,例如: ```lua os.rm("$(builddir)/inc/**.h") os.rm("$(builddir)/lib/") ``` ## os.trycp * 尝试复制文件或目录 #### 函数原型 ::: tip API ```lua os.trycp(source: , destination: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | source | 源路径或模式 | | destination | 目标路径 | #### 用法说明 跟[os.cp](#os-cp)类似,唯一的区别就是,此接口操作失败不会抛出异常中断xmake,而是通过返回值标示是否执行成功。 ```lua if os.trycp("file", "dest/file") then end ``` ## os.trymv * 尝试移动文件或目录 #### 函数原型 ::: tip API ```lua os.trymv(source: , destination: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | source | 源路径或模式 | | destination | 目标路径 | #### 用法说明 跟[os.mv](#os-mv)类似,唯一的区别就是,此接口操作失败不会抛出异常中断xmake,而是通过返回值标示是否执行成功。 ```lua if os.trymv("file", "dest/file") then end ``` ## os.tryrm * 尝试删除文件或目录 #### 函数原型 ::: tip API ```lua os.tryrm(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 文件或目录路径 | #### 用法说明 跟[os.rm](#os-rm)类似,唯一的区别就是,此接口操作失败不会抛出异常中断xmake,而是通过返回值标示是否执行成功。 ```lua if os.tryrm("file") then end ``` ## os.cd * 进入指定目录 #### 函数原型 ::: tip API ```lua os.cd(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 目录路径 | #### 用法说明 这个操作用于目录切换,同样也支持内置变量,但是不支持模式匹配和多目录处理,例如: ```lua -- 进入临时目录 os.cd("$(tmpdir)") ``` 如果要离开进入之前的目录,有多种方式: ```lua -- 进入上级目录 os.cd("..") -- 进入先前的目录,相当于:cd - os.cd("-") -- 进入目录前保存之前的目录,用于之后跨级直接切回 local oldir = os.cd("./src") ... os.cd(oldir) ``` ## os.rmdir * 仅删除目录 #### 函数原型 ::: tip API ```lua os.rmdir(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 目录路径 | #### 用法说明 如果不是目录就无法删除。 ## os.mkdir * 创建目录 #### 函数原型 ::: tip API ```lua os.mkdir(path: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 目录路径 | | ... | 可变参数,可传递多个目录路径 | #### 用法说明 支持批量创建和内置变量,例如: ```lua os.mkdir("$(tmpdir)/test", "$(builddir)/inc") ``` 支持递归创建多级目录,如果父目录不存在会自动创建。 ## os.touch * 创建空文件或更新文件时间戳 #### 函数原型 ::: tip API ```lua os.touch(path: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 文件路径 | | ... | 可变参数,可传递多个文件路径 | #### 用法说明 如果文件不存在,则创建一个空文件。如果文件已存在,则更新文件的修改时间为当前时间。 支持批量创建: ```lua os.touch("file1.txt", "file2.txt", "file3.txt") ``` ## os.isdir * 判断是否为目录 #### 函数原型 ::: tip API ```lua os.isdir(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 目录路径 | #### 用法说明 如果目录不存在,则返回false ```lua if os.isdir("src") then -- ... end ``` ## os.isfile * 判断是否为文件 #### 函数原型 ::: tip API ```lua os.isfile(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 文件路径 | #### 用法说明 如果文件不存在,则返回false ```lua if os.isfile("$(builddir)/libxxx.a") then -- ... end ``` ## os.exists * 判断文件或目录是否存在 #### 函数原型 ::: tip API ```lua os.exists(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 文件或目录路径 | #### 用法说明 如果文件或目录不存在,则返回false ```lua -- 判断目录存在 if os.exists("$(builddir)") then -- ... end -- 判断文件存在 if os.exists("$(builddir)/libxxx.a") then -- ... end ``` ## os.islink * 判断是否为符号链接 #### 函数原型 ::: tip API ```lua os.islink(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 符号链接路径 | #### 用法说明 判断指定路径是否为符号链接,如果不是符号链接或不存在则返回 false。 ```lua if os.islink("path/to/symlink") then -- 是符号链接 local target = os.readlink("path/to/symlink") print("链接目标:", target) end ``` 配合 [os.ln](#os-ln) 使用: ```lua os.ln("source.txt", "link.txt") assert(os.islink("link.txt")) ``` ## os.dirs * 遍历获取指定目录下的所有目录 #### 函数原型 ::: tip API ```lua os.dirs(pattern: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | pattern | 文件模式 | #### 用法说明 支持[add\_files](#targetadd_files)中的模式匹配,支持递归和非递归模式遍历,返回的结果是一个table数组,如果获取不到,返回空数组,例如: ```lua -- 递归遍历获取所有子目录 for _, dir in ipairs(os.dirs("$(builddir)/inc/**")) do print(dir) end ``` ## os.files * 遍历获取指定目录下的所有文件 #### 函数原型 ::: tip API ```lua os.files(pattern: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | pattern | 文件模式 | #### 用法说明 支持[add\_files](#targetadd_files)中的模式匹配,支持递归和非递归模式遍历,返回的结果是一个table数组,如果获取不到,返回空数组,例如: ```lua -- 非递归遍历获取所有子文件 for _, filepath in ipairs(os.files("$(builddir)/inc/*.h")) do print(filepath) end ``` ## os.filedirs * 遍历获取指定目录下的所有文件和目录 #### 函数原型 ::: tip API ```lua os.filedirs(pattern: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | pattern | 文件模式 | #### 用法说明 支持[add\_files](#targetadd_files)中的模式匹配,支持递归和非递归模式遍历,返回的结果是一个table数组,如果获取不到,返回空数组,例如: ```lua -- 递归遍历获取所有子文件和目录 for _, filedir in ipairs(os.filedirs("$(builddir)/**")) do print(filedir) end ``` ## os.exit * 退出程序 #### 函数原型 ::: tip API ```lua os.exit(code: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | code | 退出码(可选) | #### 用法说明 退出当前程序,并返回指定的退出码。如果不指定退出码,默认为 0(成功)。 ```lua -- 正常退出 os.exit(0) -- 异常退出 if error_occurred then os.exit(1) end ``` ## os.isexec * 判断文件是否可执行 #### 函数原型 ::: tip API ```lua os.isexec(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 文件路径 | #### 用法说明 判断指定文件是否具有可执行权限。在 Unix 系统上检查文件的执行权限位,在 Windows 上检查文件扩展名。 用于动态检测可执行文件: ```lua local program = "/usr/bin/gcc" if os.isexec(program) then print("程序可执行") os.execv(program, {"--version"}) else print("程序不可执行或不存在") end ``` ## os.run * 安静运行原生shell命令 #### 函数原型 ::: tip API ```lua os.run(command: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | command | 命令字符串 | | ... | 命令的可变参数 | #### 用法说明 用于执行第三方的shell命令,但不会回显输出,仅仅在出错后,高亮输出错误信息。 此接口支持参数格式化、内置变量,例如: ```lua -- 格式化参数传入 os.run("echo hello %s!", "xmake") -- 列举构建目录文件 os.run("ls -l $(builddir)") ``` ::: tip 注意 使用此接口执行shell命令,容易使构建跨平台性降低,对于`os.run("cp ..")`这种尽量使用`os.cp`代替 如果必须使用此接口运行shell程序,请自行使用[config.plat](#config-plat)接口判断平台支持。 ::: ## os.runv * 安静运行原生shell命令,带参数列表 #### 函数原型 ::: tip API ```lua os.runv(program: , args:
, options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | program | 程序名 | | args | 参数表 | | options | 选项表(可选) | #### 用法说明 跟[os.run](#os-run)类似,只是传递参数的方式是通过参数列表传递,而不是字符串命令,例如: ```lua os.runv("echo", {"hello", "xmake!"}) ``` 另外,此接口也支持envs参数设置: ```lua os.runv("echo", {"hello", "xmake!"}, {envs = {PATH = "xxx;xx", CFLAGS = "xx"}}) ``` ## os.exec * 回显运行原生shell命令 #### 函数原型 ::: tip API ```lua os.exec(command: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | command | 命令字符串 | | ... | 命令的可变参数 | #### 用法说明 与[os.run](#os-run)接口类似,唯一的不同是,此接口执行shell程序时,是带回显输出的,一般调试的时候用的比较多 ## os.execv * 回显运行原生shell命令,带参数列表 #### 函数原型 ::: tip API ```lua os.execv(program: , args:
, options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | program | 程序名 | | args | 参数表 | | options | 选项表(可选) | #### 用法说明 跟[os.exec](#os-exec)类似,只是传递参数的方式是通过参数列表传递,而不是字符串命令,例如: ```lua os.execv("echo", {"hello", "xmake!"}) ``` 另外,此接口还支持一个可选的参数,用于传递设置:重定向输出,执行环境变量设置,例如: ```lua os.execv("echo", {"hello", "xmake!"}, {stdout = outfile, stderr = errfile, envs = {PATH = "xxx;xx", CFLAGS = "xx"}} ``` 其中,stdout 和 stderr 参数用于传递重定向输出和错误输出,可以直接传入文件路径,也可以传入 io.open 打开的文件对象。 v2.5.1 之后的版本,我们还支持设置 stdin 参数,来支持重定向输入文件。 ::: tip 注意 stdout/stderr/stdin 可以同时支持:文件路径、文件对象、管道对象等三种类型值。 ::: ### 重定向到文件 ```lua -- 重定向输出到文件 os.execv("echo", {"hello"}, {stdout = "output.txt"}) -- 使用文件对象 local outfile = io.open("output.txt", "w") os.execv("echo", {"hello"}, {stdout = outfile}) outfile:close() ``` ### 重定向到管道 配合 pipe 模块,可以捕获子进程的输出进行处理: ```lua import("core.base.pipe") import("core.base.bytes") -- 创建管道 local rpipe, wpipe = pipe.openpair() -- 将子进程 stdout 重定向到管道 os.execv("ls", {"-l"}, {stdout = wpipe}) -- 关闭写端,读取输出 wpipe:close() local buff = bytes(8192) local read, data = rpipe:read(buff, 8192) if read > 0 then print("命令输出:", data:str()) end rpipe:close() ``` 同时重定向 stdout 和 stderr: ```lua import("core.base.pipe") import("core.base.bytes") local rpipe_out, wpipe_out = pipe.openpair() local rpipe_err, wpipe_err = pipe.openpair() -- 分别重定向标准输出和错误输出 os.execv("make", {}, {stdout = wpipe_out, stderr = wpipe_err}) wpipe_out:close() wpipe_err:close() -- 读取标准输出 local buff = bytes(8192) local read, output = rpipe_out:read(buff, 8192) print("标准输出:", output and output:str() or "") -- 读取错误输出 local read, errors = rpipe_err:read(buff, 8192) print("错误输出:", errors and errors:str() or "") rpipe_out:close() rpipe_err:close() ``` 另外,如果想在这次执行中临时设置和改写一些环境变量,可以传递envs参数,里面的环境变量设置会替换已有的设置,但是不影响外层的执行环境,只影响当前命令。 我们也可以通过`os.getenvs()`接口获取当前所有的环境变量,然后改写部分后传入envs参数。 ## os.iorun * 安静运行原生shell命令并获取输出内容 #### 函数原型 ::: tip API ```lua os.iorun(command: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | command | 命令字符串 | | options | 选项表(可选) | #### 用法说明 与[os.run](#os-run)接口类似,唯一的不同是,此接口执行shell程序后,会获取shell程序的执行结果,相当于重定向输出。 可同时获取`stdout`, `stderr`中的内容,例如: ```lua local outdata, errdata = os.iorun("echo hello xmake!") ``` ## os.iorunv * 安静运行原生shell命令并获取输出内容,带参数列表 #### 函数原型 ::: tip API ```lua os.iorunv(program: , args:
, options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | program | 程序名称 | | args | 参数列表 | | options | 选项表(可选) | #### 用法说明 跟[os.iorun](#os-iorun)类似,只是传递参数的方式是通过参数列表传递,而不是字符串命令,例如: ```lua local outdata, errdata = os.iorunv("echo", {"hello", "xmake!"}) ``` 另外,此接口也支持envs参数设置: ```lua local outdata, errdata = os.iorunv("echo", {"hello", "xmake!"}, {envs = {PATH = "xxx;xx", CFLAGS = "xx"}} ``` ## os.getenv * 获取系统环境变量 #### 函数原型 ::: tip API ```lua os.getenv(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 环境变量名 | #### 用法说明 获取指定环境变量的值。如果环境变量不存在,返回 nil。 ```lua local path = os.getenv("PATH") if path then print("PATH:", path) end -- 获取带默认值的环境变量 local home = os.getenv("HOME") or "/tmp" ``` ## os.setenv * 设置系统环境变量 #### 函数原型 ::: tip API ```lua os.setenv(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 环境变量名 | | value | 环境变量值 | #### 用法说明 设置指定环境变量的值。设置后会影响当前进程及其子进程的环境变量。 ```lua -- 设置环境变量 os.setenv("MY_VAR", "my_value") print(os.getenv("MY_VAR")) -- 输出: my_value -- 设置 PATH os.setenv("PATH", "/new/path:" .. os.getenv("PATH")) ``` ## os.tmpdir * 获取临时目录 #### 函数原型 ::: tip API ```lua os.tmpdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 跟[$(tmpdir)](/zh/api/description/builtin-variables#var-tmpdir)结果一致,只不过是直接获取返回一个变量,可以用后续字符串维护。 ```lua print(path.join(os.tmpdir(), "file.txt")) ``` 等价于: ```lua print("$(tmpdir)/file.txt") ``` ## os.tmpfile * 获取临时文件路径 #### 函数原型 ::: tip API ```lua os.tmpfile() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 生成一个唯一的临时文件路径,返回的仅是路径字符串,文件本身不会自动创建,需要自己创建。 每次调用都会生成不同的临时文件路径,适合用于创建临时文件: ```lua -- 生成临时文件路径 local tmpfile = os.tmpfile() print("临时文件:", tmpfile) -- 例如: /tmp/xmake_XXXXXX -- 创建并使用临时文件 io.writefile(tmpfile, "temporary data") -- 使用完后删除 os.rm(tmpfile) ``` ## os.curdir * 获取当前目录路径 #### 函数原型 ::: tip API ```lua os.curdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 跟[$(curdir)](/zh/api/description/builtin-variables#var-curdir)结果一致,只不过是直接获取返回一个变量,可以用后续字符串维护。 用法参考:[os.tmpdir](#os-tmpdir)。 ## os.filesize * 获取文件大小 #### 函数原型 ::: tip API ```lua os.filesize(filepath: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filepath | 文件路径 | #### 用法说明 返回文件的大小(字节数)。如果文件不存在或无法访问,返回 0。 实用示例: ```lua local size = os.filesize("build/output.bin") if size > 0 then print(string.format("文件大小: %.2f KB", size / 1024)) end -- 检查文件是否为空 if os.filesize("config.txt") == 0 then print("配置文件为空") end ``` ## os.scriptdir * 获取当前描述脚本的路径 #### 函数原型 ::: tip API ```lua os.scriptdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 跟[$(scriptdir)](/zh/api/description/builtin-variables#var-scriptdir)结果一致,只不过是直接获取返回一个变量,可以用后续字符串维护。 用法参考:[os.tmpdir](#os-tmpdir)。 ## os.programdir * 获取xmake安装主程序脚本目录 #### 函数原型 ::: tip API ```lua os.programdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 跟[$(programdir)](/zh/api/description/builtin-variables#var-programdir)结果一致,只不过是直接获取返回一个变量,可以用后续字符串维护。 ## os.programfile * 获取xmake可执行文件路径 #### 函数原型 ::: tip API ```lua os.programfile() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回 xmake 可执行文件的完整路径。 ```lua print("xmake 路径:", os.programfile()) -- 例如: /usr/local/bin/xmake ``` ## os.projectdir * 获取工程主目录 #### 函数原型 ::: tip API ```lua os.projectdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 跟[$(projectdir)](/zh/api/description/builtin-variables#var-projectdir)结果一致,只不过是直接获取返回一个变量,可以用后续字符串维护。 ## os.arch * 获取当前系统架构 #### 函数原型 ::: tip API ```lua os.arch() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回当前主机系统的默认架构。例如在 `linux x86_64` 上执行 xmake 进行构建,返回值是:`x86_64` 常见架构值:`x86_64`、`i386`、`arm64`、`armv7`、`mips` 等。 ```lua print("当前架构:", os.arch()) -- 根据架构执行不同的操作 if os.arch() == "x86_64" then add_defines("ARCH_X64") end ``` ## os.host * 获取当前主机的操作系统 #### 函数原型 ::: tip API ```lua os.host() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 跟 [$(host)](/zh/api/description/builtin-variables#var-host) 结果一致。例如在 `linux x86_64` 上执行 xmake 进行构建,返回值是:`linux` 常见系统值:`linux`、`macosx`、`windows`、`bsd` 等。 ```lua print("当前系统:", os.host()) -- 根据系统执行不同的操作 if os.host() == "windows" then add_defines("WINDOWS") elseif os.host() == "linux" then add_defines("LINUX") end ``` ## os.subhost * 获取当前子系统 #### 函数原型 ::: tip API ```lua os.subhost() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取当前子系统环境,如:在 Windows 上的 msys、cygwin 等。 如果不在子系统环境中运行,返回值与 [os.host()](#os-host) 相同。 ```lua -- 在 MSYS2 环境中 print(os.subhost()) -- 返回: msys -- 检测是否在子系统环境中 if os.subhost() ~= os.host() then print("在子系统环境中运行") end ``` ## os.subarch * 获取子系统架构 #### 函数原型 ::: tip API ```lua os.subarch() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取子系统的架构。如果不在子系统环境中运行,返回值与 [os.arch()](#os-arch) 相同。 ## os.is\_host * 判断给定系统是否为当前系统 #### 函数原型 ::: tip API ```lua os.is_host(host: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | host | 系统名称 | #### 用法说明 ```lua if os.is_host("linux") then -- 在 Linux 系统上 end if os.is_host("macosx", "linux") then -- 在 macOS 或 Linux 系统上 end ``` 支持同时判断多个系统,只要匹配其中一个就返回 true。 ::: tip 提示 推荐使用更简洁的内置接口 `is_host()`,无需 `os.` 前缀,用法一致: ```lua if is_host("linux") then -- 在 Linux 系统上 end ``` ::: ## os.is\_arch * 判断给定架构是否为当前架构 #### 函数原型 ::: tip API ```lua os.is_arch(arch: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | arch | 架构名称 | | ... | 可变参数,可以传递多个架构 | #### 用法说明 ```lua if os.is_arch("x86_64") then -- 在 x86_64 架构上 end if os.is_arch("x86_64", "arm64") then -- 在 x86_64 或 arm64 架构上 end ``` 支持同时判断多个架构。 ## os.is\_subhost * 判断给定子系统是否为当前子系统 #### 函数原型 ::: tip API ```lua os.is_subhost(subhost: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | subhost | 子系统名称 | | ... | 可变参数,可以传递多个子系统 | #### 用法说明 ```lua if os.is_subhost("msys") then -- 在 MSYS 子系统中 end ``` 用于检测是否运行在特定的子系统环境中,如 msys、cygwin 等。 ::: tip 提示 推荐使用更简洁的内置接口 `is_subhost()`,用法一致。 ::: ## os.is\_subarch * 判断给定子系统架构是否为当前子系统架构 #### 函数原型 ::: tip API ```lua os.is_subarch(subarch: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | subarch | 子系统架构名称 | | ... | 可变参数,可以传递多个子系统架构 | #### 用法说明 ```lua if os.is_subarch("x86_64") then -- 子系统架构是 x86_64 end ``` ## os.ln * 为一个文件或目录创建符号链接 #### 函数原型 ::: tip API ```lua os.ln(source: , target: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | source | 源文件或目录路径 | | target | 目标符号链接路径 | #### 用法说明 ```lua -- 创建一个指向 "tmp.txt" 文件的符号链接 "tmp.txt.ln" os.ln("xxx.txt", "xxx.txt.ln") ``` ## os.readlink * 读取符号链接内容 #### 函数原型 ::: tip API ```lua os.readlink(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 符号链接路径 | #### 用法说明 ```lua local target = os.readlink("path/to/symlink") ``` 读取符号链接指向的目标路径。如果指定的路径不是符号链接,则返回 nil。 配合 [os.ln](#os-ln) 和 [os.islink](#os-islink) 使用: ```lua os.ln("source.txt", "link.txt") if os.islink("link.txt") then local target = os.readlink("link.txt") print("链接指向:", target) -- 输出: source.txt end ``` ## os.raise * 抛出一个异常并且中止当前脚本运行 #### 函数原型 ::: tip API ```lua os.raise(message: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | message | 错误信息字符串 | #### 用法说明 ```lua -- 抛出一个带 "an error occurred" 信息的异常 os.raise("an error occurred") ``` ::: tip 注意 推荐使用与 `os.raise` 等价的内置接口 `raise`,用法与 `os.raise` 一致 ::: ## os.raiselevel * 与 [os.raise](#os-raise) 类似但是可以指定异常等级 #### 函数原型 ::: tip API ```lua os.raiselevel(level: , message: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | level | 异常等级 | | message | 异常信息 | #### 用法说明 ```lua -- 抛出一个带 "an error occurred" 信息的异常 os.raiselevel(3, "an error occurred") ``` ## os.features * 获取系统特性 #### 函数原型 ::: tip API ```lua os.features() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取当前操作系统支持的特性列表。返回一个 table,包含系统支持的各种特性。 ## os.getenvs * 获取所有当前系统变量 #### 函数原型 ::: tip API ```lua os.getenvs() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua local envs = os.getenvs() -- home directory (on linux) print(envs["HOME"]) ``` ## os.setenvs * 使用给定系统变量替换当前所有系统变量,并返回旧系统变量 #### 函数原型 ::: tip API ```lua os.setenvs(envs:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | envs | 环境变量表 | #### 用法说明 ## os.addenvs * 向当前系统变量添加新变量,并且返回所有旧系统变量 #### 函数原型 ::: tip API ```lua os.addenvs(envs:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | envs | 要添加的环境变量表 | #### 用法说明 ```lua os.setenvs({EXAMPLE = "a/path"}) -- add a custom variable to see addenvs impact on it local oldenvs = os.addenvs({EXAMPLE = "some/path/"}) print(os.getenvs()["EXAMPLE"]) --got some/path/;a/path print(oldenvs["EXAMPLE"]) -- got a/path ``` ## os.joinenvs * 拼接系统变量,与 [os.addenvs](#os-addenvs) 类似,但是不会对当前环境变量产生影响,若第二个参数为 `nil`,则使用原有环境变量 #### 函数原型 ::: tip API ```lua os.joinenvs(envs1:
, envs2:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | envs1 | 第一个环境变量表 | | envs2 | 第二个环境变量表 | #### 用法说明 ```lua local envs0 = {CUSTOM = "a/path"} local envs1 = {CUSTOM = "some/path/"} print(os.joinenvs(envs0, envs1)) -- result is : { CUSTION = "a/path;some/path/" } ``` ## os.addenv * 向指定环境变量添加值 #### 函数原型 ::: tip API ```lua os.addenv(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 环境变量名 | | value | 要添加的值 | #### 用法说明 向指定的环境变量追加新值,使用系统默认的分隔符(Unix 上是 `:`,Windows 上是 `;`)。 ```lua -- 向 PATH 添加新路径 os.addenv("PATH", "/usr/local/bin") -- 验证 print(os.getenv("PATH")) -- 新路径会被追加到现有 PATH 中 ``` ## os.setenvp * 使用给定分隔符设置环境变量 #### 函数原型 ::: tip API ```lua os.setenvp(name: , value: , separator: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 环境变量名 | | value | 环境变量值 | | separator | 分隔符字符串 | #### 用法说明 设置环境变量,使用指定的分隔符。与 [os.setenv](#os-setenv) 类似,但可以自定义分隔符。 ## os.addenvp * 使用给定分隔符向环境变量添加值 #### 函数原型 ::: tip API ```lua os.addenvp(name: , value: , separator: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 环境变量名 | | value | 要添加的值 | | separator | 分隔符字符串 | #### 用法说明 向环境变量追加值,使用指定的分隔符。与 [os.addenv](#os-addenv) 类似,但可以自定义分隔符。 ## os.workingdir * 获取工作目录 #### 函数原型 ::: tip API ```lua os.workingdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取当前工作目录的绝对路径。与 `os.curdir()` 类似,但返回的是工作目录而不是当前脚本执行目录。 ```lua print("工作目录:", os.workingdir()) ``` ## os.isroot * 判断xmake是否以管理员权限运行 #### 函数原型 ::: tip API ```lua os.isroot() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 在 Unix 系统上检查是否以 root 用户运行,在 Windows 上检查是否以管理员权限运行。 某些操作需要管理员权限时很有用: ```lua if not os.isroot() then raise("此操作需要管理员权限,请使用 sudo 或以管理员身份运行") end ``` ## os.fscase * 判断操作系统的文件系统是否大小写敏感 #### 函数原型 ::: tip API ```lua os.fscase() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回 true 表示文件系统区分大小写(如 Linux),false 表示不区分(如 Windows、macOS 默认)。 用于处理跨平台的文件名兼容性: ```lua if not os.fscase() then -- 在不区分大小写的系统上,避免使用仅大小写不同的文件名 print("警告: 文件系统不区分大小写") end ``` ## os.term * 获取当前终端 (windows-terminal, vscode, xterm, ...) #### 函数原型 ::: tip API ```lua os.term() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## os.shell * 获取当前shell #### 函数原型 ::: tip API ```lua os.shell() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取当前使用的 shell 程序名称,支持多种 shell 类型如 pwsh, cmd, bash, zsh 等。 ## os.cpuinfo * 获取当前CPU信息 #### 函数原型 ::: tip API ```lua os.cpuinfo(key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | CPU信息键名(可选) | #### 用法说明 ```lua print(os.cpuinfo()) -- probably got { -- march = "Alder Lake", -- model = 154, -- ncpu = 20, -- model_name = "12th Gen Intel(R) Core(TM) i9-12900H", -- usagerate = 0.041839182376862, -- vendor = "GenuineIntel", -- family = 6 -- } print(os.cpuinfo("march")) -- probably got "Alder Lake" ``` ## os.meminfo * 获取内存信息 #### 函数原型 ::: tip API ```lua os.meminfo(key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 内存信息键名(可选) | #### 用法说明 ```lua print(os.meminfo()) -- probably got { -- pagesize = 4096, -- usagerate = 0.60694103194103, -- availsize = 12798, -- totalsize = 32560 -- } print(os.meminfo("pagesize")) -- probably got 4096 ``` ## os.default\_njob * 获取默认编译任务数 #### 函数原型 ::: tip API ```lua os.default_njob() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回默认的并行编译任务数,通常等于 CPU 核心数。 ```lua local njob = os.default_njob() print("默认并行任务数:", njob) ``` ## os.argv * 将命令行字符串解析为参数列表 #### 函数原型 ::: tip API ```lua os.argv(command: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | command | 命令行字符串 | #### 用法说明 将命令行字符串解析为参数数组,支持引号、转义字符等复杂格式。 解析规则: * 支持双引号和单引号包裹参数 * 支持转义字符(`\`) * 自动处理空格分隔 * 处理特殊字符如括号、反斜杠等 示例: ```lua -- 简单参数 os.argv("aa bb cc") -- 返回: {"aa", "bb", "cc"} -- 带引号的参数 os.argv('"aa bb cc" dd') -- 返回: {"aa bb cc", "dd"} -- 带等号的参数 os.argv("--bb=bbb -c") -- 返回: {"--bb=bbb", "-c"} -- 转义引号 os.argv('-DTEST=\\"hello\\"') -- 返回: {'-DTEST="hello"'} -- 复杂参数 os.argv('-DTEST="hello world"') -- 返回: {'-DTEST=hello world'} ``` 支持 `splitonly` 选项仅分割不处理引号: ```lua os.argv('-DTEST="hello world"', {splitonly = true}) -- 返回: {'-DTEST="hello world"'} ``` ## os.args * 将参数列表转换为命令行字符串 #### 函数原型 ::: tip API ```lua os.args(args:
, options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | args | 参数数组 | | options | 选项表(可选) | #### 用法说明 将参数数组转换为命令行字符串,是 [os.argv](#os-argv) 的逆操作。 自动处理特殊字符: * 含有空格的参数会自动加引号 * 自动转义特殊字符 * 处理路径中的反斜杠 示例: ```lua -- 简单参数 os.args({"aa", "bb", "cc"}) -- 返回: "aa bb cc" -- 含空格的参数 os.args({"aa bb cc", "dd"}) -- 返回: '"aa bb cc" dd' -- 带引号的参数 os.args({'-DTEST="hello"'}) -- 返回: '-DTEST=\\"hello\\"' -- 路径参数 os.args({"aa\\bb/cc dd", "ee"}) -- 返回: '"aa\\\\bb/cc dd" ee' ``` 支持 `escape` 选项启用额外的转义: ```lua os.args({"aa\\bb/cc", "dd"}, {escape = true}) -- 返回: "aa\\\\bb/cc dd" ``` 配合 `os.argv` 进行往返转换: ```lua local cmdline = "gcc -o test test.c" local args = os.argv(cmdline) local cmdline2 = os.args(args) -- cmdline2 应该与 cmdline 等价 ``` ## os.mclock * 获取单调时钟时间(毫秒) #### 函数原型 ::: tip API ```lua os.mclock() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回单调递增的时间戳(毫秒),适合用于测量时间间隔。 与 `os.clock()` 不同,`os.mclock()` 返回的是单调时钟,不受系统时间调整的影响,更适合用于性能测量: ```lua local function benchmark(func) local start = os.mclock() func() local elapsed = os.mclock() - start print(string.format("执行耗时: %.2f ms", elapsed)) end benchmark(function() os.sleep(100) end) ``` --- --- url: /api/description/package-dependencies.md --- # Package Dependencies ## package * Define package configuration #### Function Prototype ::: tip API ```lua package(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Package name string | #### Usage 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:add("includedirs", "include/libxml2") package:add("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) ``` ## set\_homepage * Set package homepage #### Function Prototype ::: tip API ```lua set_homepage(url: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | url | Package homepage URL string | #### Usage Set the official page address of the project where the package is located. ## set\_description * Set package description #### Function Prototype ::: tip API ```lua set_description(description: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | description | Package description string | #### Usage Set the package description information, generally see the relevant package information through `xmake require --info zlib`. ## set\_kind * Set package kind #### Function Prototype ::: tip API ```lua set_kind(kind: , { headeronly = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | kind | Package type: "library", "binary", "toolchain" | | headeronly | For library type, whether it's header-only library | #### Usage Used to set the package type. xmake packages currently support the following types: ### library This is the default package type and usually does not need to be explicitly configured. Used for regular library packages, including static and dynamic libraries. ```lua package("zlib") -- library type, no need to set explicitly set_homepage("http://www.zlib.net") set_description("A Massively Spiffy Yet Delicately Unobtrusive Compression Library") ``` For header-only libraries (libraries that contain only header files), explicit configuration is required: ```lua package("fmt") set_kind("library", {headeronly = true}) set_homepage("https://fmt.dev") set_description("A modern formatting library") ``` ### binary Used for executable program packages. These packages provide executable files after installation and generally run on the current compilation host system. ```lua package("cmake") set_kind("binary") set_homepage("https://cmake.org") set_description("A cross-platform family of tool designed to build, test and package software") ``` ### toolchain Used for complete compilation toolchain packages. These packages contain complete compilation toolchains (such as compilers, linkers, etc.) and can be used with `set_toolchains` + `add_requires` to achieve automatic toolchain download and binding. ```lua package("llvm") set_kind("toolchain") set_homepage("https://llvm.org/") set_description("The LLVM Compiler Infrastructure") ``` Example of using a toolchain package: ```lua add_rules("mode.debug", "mode.release") add_requires("llvm 14.0.0", {alias = "llvm-14"}) target("test") set_kind("binary") add_files("src/*.c") set_toolchains("llvm@llvm-14") ``` ## set\_urls * Set package urls #### Function Prototype ::: tip API ```lua set_urls(urls: , ..., { excludes = , version = , http_headers = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | urls | Package source URL string or array | | ... | Variable parameters, can pass multiple URLs | | excludes | Files to exclude from extraction | | version | Version transformation function | | http\_headers | HTTP headers for download | #### Usage Set the source package or git repository address of the package. Unlike add\_urls, this interface is the override setting, and add\_urls is the additional setting. Other usage methods are similar. This is chosen according to different needs. ## add\_urls * Add package urls #### Function Prototype ::: tip API ```lua add_urls(urls: , ..., { alias = , excludes = , version = , http_headers = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | urls | Package source URL string or array | | ... | Variable parameters, can pass multiple URLs | | alias | URL alias for different sources | | excludes | Files to exclude from extraction | | version | Version transformation function | | http\_headers | HTTP headers for download | #### Usage Add the source package of the package or the git repository address. This interface is generally paired with add\_version to set the version of each source package and the corresponding sha256 value. ::: tip NOTE You can add multiple urls as the mirror source, xmake will automatically detect the fastest url for download, and if the download fails, try other urls. ::: ```lua add_urls("https://github.com/protobuf-c/protobuf-c/releases/download/v$(version)/protobuf-c-$(version).tar.gz") add_versions("1.3.1", "51472d3a191d6d7b425e32b612e477c06f73fe23e07f6a6a839b11808e9d2267") ``` The `$(version)` built-in variable in urls will be adapted according to the version selected during the actual installation, and the version number is selected from the list of versions specified in `add_versions`. If there is a more complicated version string for urls and there is no direct correspondence with add\_versions, you need to customize the conversion in the following way: ```lua add_urls("https://sqlite.org/2018/sqlite-autoconf-$(version)000.tar.gz", {version = function (version) return version:gsub("%.", "") end}) add_versions("3.24.0", "d9d14e88c6fb6d68de9ca0d1f9797477d82fc3aed613558f87ffbdbbc5ceb74a") add_versions("3.23.0", "b7711a1800a071674c2bf76898ae8584fc6c9643cfe933cfc1bc54361e3a6e49") ``` Of course, we can only add the git source address: ```lua add_urls("https://gitlab.gnome.org/GNOME/libxml2.git") ``` If the source code package sha256 corresponding to multiple mirror addresses is different, we can set them separately by means of alias: ```lua add_urls("https://ffmpeg.org/releases/ffmpeg-$(version).tar.bz2", {alias = "home"}) add_urls("https://github.com/FFmpeg/FFmpeg/archive/n$(version).zip", {alias = "github"}) add_versions("home:4.0.2", "346c51735f42c37e0712e0b3d2f6476c86ac15863e4445d9e823fe396420d056") add_versions("github:4.0.2", "4df1ef0bf73b7148caea1270539ef7bd06607e0ea8aa2fbf1bb34062a097f026") ``` We can also set the http headers for the specified urls: ```lua add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", { http_headers = {"TEST1: foo", "TEST2: bar"} }) ``` ## add\_versions * Add package versions #### Function Prototype ::: tip API ```lua add_versions(version: , hash: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Package version string | | hash | SHA256 hash value for verification | #### Usage Used to set the version of each source package and the corresponding sha256 value, as described in [add\_urls](#add_urls) ## add\_versionfiles * Adding a list of package versions #### Function Prototype ::: tip API ```lua add_versionfiles(file: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | file | Version file path containing version and hash pairs | #### Usage Normally we can add package versions through the `add_versions` interface, but if there are more and more versions, the package configuration will be too bloated, at this time, we can use the `add_versionfiles` interface to store a list of all the versions in a separate file to maintain. For example: ```lua package("libcurl") add_versionfiles("versions.txt") ``` ```sh 8.5.0 ce4b6a6655431147624aaf582632a36fe1ade262d5fab385c60f78942dd8d87b 8.4.0 e5250581a9c032b1b6ed3cf2f9c114c811fc41881069e9892d115cc73f9e88c6 8.0.1 9b6b1e96b748d04b968786b6bdf407aa5c75ab53a3d37c1c8c81cdb736555ccf 7.87.0 5d6e128761b7110946d1276aff6f0f266f2b726f5e619f7e0a057a474155f307 7.31.0 a73b118eececff5de25111f35d1d0aafe1e71afdbb83082a8e44d847267e3e08 ... ``` ## add\_patches * Add package patches #### Function Prototype ::: tip API ```lua add_patches(version: , url: , hash: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Package version for which patch applies | | url | Patch file URL | | hash | SHA256 hash value for patch verification | #### Usage This interface is used for the source code package. Before compiling and installing, firstly set the corresponding patch package, compile it, and support multiple patches at the same time. ```lua if is_plat("macosx") then     add_patches("1.15", "https://raw.githubusercontent.com/Homebrew/patches/9be2793af/libiconv/patch-utf8mac.diff",                         "e8128732f22f63b5c656659786d2cf76f1450008f36bcf541285268c66cabeab") end ``` For example, the above code, when compiled for macosx, is marked with the corresponding patch-utf8mac.diff patch, and each patch is also set to the value of sha256 to ensure integrity. ## add\_links * Add package links #### Function Prototype ::: tip API ```lua add_links(links: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | links | Library link name string or array | | ... | Variable parameters, can pass multiple link names | #### Usage By default, xmake will automatically detect the installed libraries and set the link relationship, but sometimes it is not very accurate. If you want to manually adjust the link order and the link name, you can set it through this interface. ```lua add_links("mbedtls", "mbedx509", "mbedcrypto") ``` ## add\_syslinks * Add system library links #### Function Prototype ::: tip API ```lua add_syslinks(syslinks: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | syslinks | System library name string or array | | ... | Variable parameters, can pass multiple system library names | #### Usage Add some system library links. When some packages integrate links, you also need to rely on some system libraries to link them. This time you can attach them to the package description. ```lua if is_plat("macosx") then     add_frameworks("CoreGraphics", "CoreFoundation", "Foundation") elseif is_plat("windows") then     add_defines("CAIRO_WIN32_STATIC_BUILD=1")     add_syslinks("gdi32", "msimg32", "user32") else     add_syslinks("pthread") end ``` ## add\_linkorders * Adjust the link order within the package #### Function Prototype ::: tip API ```lua add_linkorders(orders: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | orders | Link order string or array | | ... | Variable parameters, can pass multiple order specifications | #### Usage For specific details, please see the target's internal documentation for `add_linkorders`, [target:add\_linkorders](/api/description/project-target#add-linkorders). ```lua package("libpng") add_linkorders("png16", "png", "linkgroup::foo") add_linkgroups("dl", {name = "foo", group = true}) ``` ## add\_linkgroups * Configure the link group of the package #### Function Prototype ::: tip API ```lua add_linkgroups(groups: , ..., { name = , group = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | groups | Link group name string or array | | ... | Variable parameters, can pass multiple group names | | name | Group name for linking | | group | Whether to treat as a group | #### Usage For specific details, please see the target's internal documentation for `add_linkgroups`, [target:add\_linkgroups](/api/description/project-target#add-linkgroups). ```lua package("libpng") add_linkorders("png16", "png", "linkgroup::foo") add_linkgroups("dl", {name = "foo", group = true}) ``` ## add\_frameworks * Add frameworks #### Function Prototype ::: tip API ```lua add_frameworks(frameworks: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | frameworks | Framework name string or array | | ... | Variable parameters, can pass multiple framework names | #### Usage Add a dependent system frameworks link. See for example: [add\_syslinks](#add_syslinks) ## add\_linkdirs * Add link directories #### Function Prototype ::: tip API ```lua add_linkdirs(dirs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dirs | Link directory path string or array | | ... | Variable parameters, can pass multiple directory paths | #### Usage The package's link library search directory can also be adjusted, but it is usually not needed, unless some libraries are not installed under prefix/lib, but in the lib subdirectory, the default search is not available. ## add\_includedirs * Add include directories #### Function Prototype ::: tip API ```lua add_includedirs(dirs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dirs | Include directory path string or array | | ... | Variable parameters, can pass multiple directory paths | #### Usage Add another header file search directory. ## add\_bindirs * Add executable file directory #### Function Prototype ::: tip API ```lua add_bindirs(dirs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dirs | Executable directory path string or array | | ... | Variable parameters, can pass multiple directory paths | #### Usage By default, if `set_kind("binary")` or `set_kind("toolchain")` is configured as an executable package. Then, it will use the bin directory as an executable directory by default and automatically add it to the PATH environment variable. If you want to open some of the compiled executable tools in the library package to users, you need to configure `package:addenv("PATH", "bin")` in the package. If you use this interface to configure `add_bindirs("bin")`, bin will be automatically added to PATH, and you no longer need to configure PATH separately. In addition, this also provides a way to modify the executable directory. ## add\_defines * Add definition #### Function Prototype ::: tip API ```lua add_defines(defines: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | defines | Macro definition string or array | | ... | Variable parameters, can pass multiple definitions | #### Usage Some specific definition options can be exported to the integrated package. ## add\_configs * Add package configs #### Function Prototype ::: tip API ```lua add_configs(name: , { description = , default = , values = , type = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration parameter name | | description | Configuration description string | | default | Default value for the configuration | | values | Allowed values array | | type | Configuration type: "string", "boolean", "number" | #### Usage We can add the external output configuration parameters of each package through this interface: ```lua package("pcre2") set_homepage("https://www.pcre.org/") set_description("A Perl Compatible Regular Expressions Library") add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}}) on_load(function (package) local bitwidth = package:config("bitwidth") or "8" package:add("links", "pcre2-" .. bitwidth) package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth) end) ``` In the engineering project, we can also view a list of configurable parameters and values for a particular package: ```sh $ xmake require --info pcre2 The package info of project: require(pcre2): -> description: A Perl Compatible Regular Expressions Library -> version: 10.31 ... -> configs: -> bitwidth: -> description: Set the code unit width. -> values: {"8","16","32"} -> default: 8 ``` Then in the project, enable these configurations and compile the package with the specific configuration: ```lua add_requires("pcre2", {configs = {bitwidth = 16}}) ``` ## add\_extsources * Add external package sources #### Function Prototype ::: tip API ```lua add_extsources(sources: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sources | External source string or array, format: "pkgconfig::name" or "brew::name" | | ... | Variable parameters, can pass multiple external sources | #### Usage Starting from version 2.5.2, we have also added two configuration interfaces `add_extsources` and `on_fetch`, which can better configure xmake to search for system libraries during the process of installing C/C++ packages. As for the specific background, we can give an example. For example, we added a package of `package("libusb")` to the [xmake-repo](https://github.com/xmake-io/xmake-repo) repository . Then users can directly integrate and use it in the following ways: ```lua add_requires("libusb") target("test") set_kind("binary") add_files("src/*.c") add_packages("libusb") ``` If libusb is not installed on the user's system, xmake will automatically download the libusb library source code, automatically compile, install and integrate, and there is no problem. But if the user installs the libusb library to the system through `apt install libusb-1.0`, then xmake should automatically search for the libusb package installed by the user in the system environment first, and use it directly, avoiding additional download, compilation and installation. But here comes the problem, xmake internally passes `find_package("libusb")` and fails to find it. Why is that? Because the package name of libusb installed via apt is `libusb-1.0`, not libusb. We can only find it through `pkg-config --cflags libusb-1.0`, but the default find\_package logic inside xmake doesn't know the existence of `libusb-1.0`, so it can't be found. Therefore, in order to better adapt to the search of system libraries in different system environments, we can use `add_extsources("pkgconfig::libusb-1.0")` to let xmake improve the search logic, for example: ```lua package("libusb") add_extsources("pkgconfig::libusb-1.0") on_install(function (package) - ... end) ``` In addition, we can also use this method to improve the search for packages installed by other package managers such as homebrew/pacman, for example: `add_extsources("pacman::libusb-1.0")`. ## add\_deps * Add package dependencies #### Function Prototype ::: tip API ```lua add_deps(deps: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | deps | Dependency package name string or array | | ... | Variable parameters, can pass multiple dependency names | #### Usage This interface allows us to automatically install all dependencies of a package when we install it by configuring the dependencies between packages. Also, by default, cmake/autoconf will automatically find the libraries and headers of all dependent packages as soon as we have configured the dependencies. Of course, if for some special reason the cmake script for the current package does not find the dependencies properly, then we can also force the dependencies to be typed in with `{packagedeps = "xxx"}`. Example. ```lua package("foo") add_deps("cmake", "bar") on_install(function (package) local configs = {} import("package.tools.cmake").install(package, configs) end) ``` The foo package is maintained using CMakeLists.txt and it relies on the bar package during installation, so xmake will install bar first and have cmake.install automatically find the bar installed library when it calls cmake. However, if foo's CMakeLists.txt still does not automatically find bar, then we can change it to the following configuration to force bar's includedirs/links etc. to be passed into foo by way of flags. ```lua package("foo") add_deps("cmake", "bar") on_install(function (package) local configs = {} import("package.tools.cmake").install(package, configs, {packagedeps = "bar"}) end) ``` ## add\_components * Add package components #### Function Prototype ::: tip API ```lua add_components(components: , ..., { deps = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | components | Component name string or array | | ... | Variable parameters, can pass multiple component names | | deps | Component dependencies array | #### Usage This is a new interface added in 2.7.3 to support componentized configuration of packages, see: [#2636](https://github.com/xmake-io/xmake/issues/2636) for details. With this interface we can configure the list of components that are actually available for the current package. ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") ``` On the user side, we can use package specific components in the following way. ```lua add_requires("sfml") target("test") add_packages("sfml", {components = "graphics") ``` ::: tip NOTE In addition to configuring the list of available components, we also need to configure each component in detail for it to work properly, so it is usually used in conjunction with the `on_component` interface. ::: A full example of the configuration and use of package components can be found at: [components example](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua) ## set\_base * Inherit package configuration #### Function Prototype ::: tip API ```lua set_base(package: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Base package name to inherit from | #### Usage This is a newly added interface in 2.6.4, through which we can inherit all the configuration of an existing package, and then rewrite some of the configuration on this basis. This is usually in the user's own project, it is more useful to modify the built-in package of the xmake-repo official repository, such as: repairing and changing urls, modifying the version list, installation logic, etc. For example, modify the url of the built-in zlib package to switch to your own zlib source address. ```lua package("myzlib") set_base("zlib") set_urls("https://github.com/madler/zlib.git") package_end() add_requires("myzlib", {system = false, alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` We can also use it to simply add an alias package. ```lua package("onetbb") set_base("tbb") ``` We can install the tbb package through `add_requires("onetbb")` integration, but the package name is different. ## on\_load * Load package configuration #### Function Prototype ::: tip API ```lua on_load(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Package load script function with package parameter | #### Usage This is an optional interface. If you want to be more flexible and dynamically judge various platform architectures, you can do it in this way, for example: ```lua on_load(function (package)     Local bitwidth = package:config("bitwidth") or "8"     package:add("links", "pcre" .. (bitwidth ~= "8" and bitwidth or ""))     If not package:config("shared") then         package:add("defines", "PCRE_STATIC")     end end) ``` The pcre package needs to do some judgment on the bitwidth to determine the name of the link library for external output. It also needs to add some defines to the dynamic library. This time, it is more flexible when set in on\_load. To find out what methods are available to `package` look [here](/api/scripts/package-instance). ## on\_fetch * Fetch package libraries #### Function Prototype ::: tip API ```lua on_fetch(platforms: , ..., script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | platforms | Platform filter string or array, optional | | ... | Variable parameters, can pass multiple platform filters | | script | Fetch script function with package and opt parameters | #### Usage This is an optional configuration. After 2.5.2, if the system libraries installed under different systems only have different package names, then using `add_extsources` to improve the system library search is sufficient, simple and convenient. However, if some packages are installed in the system, the location is more complicated. To find them, some additional scripts may be needed. For example: access to the registry under windows to find packages, etc. At this time, we can use `on_fetch `Fully customized search system library logic. Let's take libusb as an example. Instead of `add_extsources`, we can use the following method to achieve the same effect. Of course, we can do more things in it. ```lua package("libusb") on_fetch("linux", function(package, opt) if opt.system then return find_package("pkgconfig::libusb-1.0") end end) ``` To find out what methods are available to `package` look [here](/api/scripts/package-instance). ## on\_check * Check whether the package supports the current platform #### Function Prototype ::: tip API ```lua on_check(platforms: , ..., script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | platforms | Platform filter string or array, optional | | ... | Variable parameters, can pass multiple platform filters | | script | Check script function with package parameter | #### Usage Sometimes, simply using `on_install("windows", "android", function () end)` cannot properly limit the package's support for the current platform. For example, it is also compiled using msvc on windows, but it only supports using the vs2022 tool chain. Then we cannot simply restrict the installation of packages by disabling the windows platform. Because each user's compilation tool chain environment may be different. At this time, we can configure `on_check` to do more detailed detection to determine whether the package supports the current tool chain environment. If the package is not supported, it will prompt the user earlier before the package is downloaded and installed. It can also avoid some unsupported ci job tests on the ci of xmake-repo. For example, the following configuration can determine whether the current msvc provides the corresponding vs sdk version. If the version is not satisfied, the package cannot be compiled and installed, and the user will see a more readable unsupported error message. ```lua package("test") on_check("windows", function (package) import("core.tool.toolchain") import("core.base.semver") local msvc = toolchain.load("msvc", {plat = package:plat(), arch = package:arch()}) if msvc then local vs_sdkver = msvc:config("vs_sdkver") assert(vs_sdkver and semver.match(vs_sdkver):gt("10.0.19041"), "package(cglm): need vs_sdkver > 10.0.19041.0") end end) ``` For example, we can also use it to determine the current compiler's support for c++20, if it does not support std::input\_iterator, which is only available in c++20. Then there is no need to continue downloading, compiling and installing this package. Users will see a `Require at least C++20.` error to prompt them to upgrade their compiler. ```lua package("test") on_check(function (package) assert(package:check_cxxsnippets({test = [[ #include #include struct SimpleInputIterator { using difference_type = std::ptrdiff_t; using value_type = int; int operator*() const; SimpleInputIterator& operator++(); void operator++(int) { ++*this; } }; static_assert(std::input_iterator); ]]}, {configs = {languages = "c++20"}}), "Require at least C++20.") end) ``` ## on\_install * Installation package #### Function Prototype ::: tip API ```lua on_install(platforms: , ..., script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | platforms | Platform filter string or array, optional | | ... | Variable parameters, can pass multiple platform filters | | script | Install script function with package parameter | #### Usage This interface is mainly used to add installation scripts. The previous string parameters are used to set supported platforms. Other script fields such as `on_load`, `on_test` are also supported. ### Platform filtering The complete filtering syntax is as follows: `plat|arch1,arch2@host|arch1,arch2` It looks very complicated, but it is actually very simple. Each stage is optional and can be partially omitted, corresponding to: `Compilation platform|Compilation architecture@Host platform|Host architecture` If you do not set any platform filter conditions, all platforms will be supported by default, and the scripts inside will take effect on all platforms, for example: ```lua on_install(function (package) -- TODO end) ``` If the installation script is effective for a specific platform, then directly specify the corresponding compilation platform. You can specify multiple ones at the same time: ```lua on_install("linux", "macosx", function (package) -- TODO end) ``` If you need to subdivide it into a specific architecture to take effect, you can write like this: ```lua on_install("linux|x86_64", "iphoneos|arm64", function (package) -- TODO end) ``` If you also want to limit the execution host environment platform and architecture, you can append `@host|arch` behind, for example: ```lua on_install("mingw@windows", function (package) -- TODO end) ``` This means that it only takes effect when compiling the mingw platform under windows. We can also not specify which platform and architecture, but only set the host platform and architecture. This is usually used to describe some dependency packages related to compilation tools, which can only be run in the host environment. For example, the package we compile depends on cmake and needs to add the cmake package description. Then the compilation and installation environment can only be the host platform: ```lua on_install("@windows", "@linux", "@macosx", function (package) -- TODO end) ``` Some other examples: ```lua -- `@linux` -- `@linux|x86_64` -- `@macosx,linux` -- `android@macosx,linux` -- `android|armeabi-v7a@macosx,linux` -- `android|armeabi-v7a@macosx,linux|x86_64` -- `android|armeabi-v7a@linux|x86_64` ``` In 2.8.7, we have improved pattern matching support and added the ability to exclude specific platforms and architectures, such as: ``` !plat|!arch@!subhost|!subarch ``` ```lua @!linux @!linux|x86_64 @!macosx,!linux !android@macosx,!linux android|!armeabi-v7a@macosx,!linux android|armeabi-v7a,!iphoneos@macosx,!linux|x86_64 !android|armeabi-v7a@!linux|!x86_64 !linux|* ``` At the same time, a built-in `native` architecture is also provided to match the local architecture of the current platform, mainly used to specify or exclude cross-compilation platforms. ```lua on_install("macosx|native", ...) ``` The above configuration, if used on a macOS x86\_64 device, will only match the local architecture compilation of `xmake f -a x86_64`. If it is cross-compiled with `xmake f -a arm64`, it will not be matched. In the same way, if you only want to match cross-compilation, you can use `macosx|!native` to negate and exclude. In version 2.9.1, we have continued to improve it and added support for conditional logic judgments: For example: ```lua on_install("!wasm|!arm* and !cross|!arm*", function (package) end) ``` To describe the arm architecture excluding the wasm and cross platforms. Moreover, it also supports nested logic described by `()`, `a and b or (a and (c or d))`. ### Compilation tools We have built-in scripts for installing common compilation tools to provide convenient architecture support for build tool chains with different source code dependencies, such as: autoconf, cmake, meson, etc. ### xmake If it is a dependency package based on xmake, it is very simple to integrate it. xmake has very good built-in integration support for it, and can directly support cross-platform compilation. Generally, you only need: ```lua on_install(function (package) import("package.tools.xmake").install(package) end) ``` If you want to pass some unique compilation configuration parameters: ```lua on_install(function (package) import("package.tools.xmake").install(package, {"--xxx=y"}) end) ``` ### cmake If it is a package based on cmake, it is very simple to integrate. Usually you only need to set some configuration parameters, but you also need to add the cmake dependency first: ```lua add_deps("cmake") on_install(function (package) import("package.tools.cmake").install(package, {"-Dxxx=ON"}) end) ``` ### autoconf If it is an autoconf-based package, the integration method is similar to cmake, except that the configuration parameters passed are different. However, usually, Unix systems have built-in autoconf series tools, so it is fine without adding related dependencies. ```lua on_install(function (package) import("package.tools.autoconf").install(package, {"--enable-shared=no"}) end) ``` However, some source code packages may not be fully satisfied by the system's built-in autoconf, so you can add the autoconf series dependencies to build them: ```lua add_deps("autoconf", "automake", "libtool", "pkg-config") on_install(function (package) import("package.tools.autoconf").install(package, {"--enable-shared=no"}) end) ``` ### meson If it is meson, you also need to add ninja dependencies to execute the build. ```lua add_deps("meson", "ninja") on_install(function (package) import("package.tools.meson").install(package, {"-Dxxx=ON"}) end) ``` ## on\_download * Custom download package #### Function Prototype ::: tip API ```lua on_download(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Download script function with package and opt parameters | #### Usage The download logic of the custom package, which is a new interface added in 2.6.4, is usually not used, and it is enough to use the built-in download of Xmake. If the user builds a private repository and has a more complex authentication mechanism and special processing logic for the download of the package, the internal download logic can be rewritten to achieve this. ```lua on_download(function (package, opt) local url = opt.url local sourcedir = opt.sourcedir -- download url to the current directory -- and extract it's source code to sourcedir -- ... end) ``` In the opt parameter, you can get the destination source directory `opt.sourcedir` of the downloaded package. We only need to get the package address from `opt.url` and download it. Then, add some custom processing logic as needed. In addition, you can add download cache processing and so on. The following is an example of custom downloading a tar.gz file, and implementing caching and decompression of source file directories, you can refer to the following: ```lua package("zlib") add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz") add_versions("v1.2.10", "42cd7b2bdaf1c4570e0877e61f2fdc0bce8019492431d054d3d86925e5058dc5") on_download(function (package, opt) import("net.http") import("utils.archive") local url = opt.url local sourcedir = opt.sourcedir local packagefile = path.filename(url) local sourcehash = package:sourcehash(opt.url_alias) local cached = true if not os.isfile(packagefile) or sourcehash ~= hash.sha256(packagefile) then cached = false -- attempt to remove package file first os.tryrm(packagefile) http.download(url, packagefile) -- check hash if sourcehash and sourcehash ~= hash.sha256(packagefile) then raise("unmatched checksum, current hash(%s) != original hash(%s)", hash.sha256(packagefile):sub(1, 8), sourcehash:sub(1, 8)) end end -- extract package file local sourcedir_tmp = sourcedir .. ".tmp" os.rm(sourcedir_tmp) if archive.extract(packagefile, sourcedir_tmp) then os.rm(sourcedir) os.mv(sourcedir_tmp, sourcedir) else -- if it is not archive file, we need only create empty source file and use package:originfile() os.tryrm(sourcedir) os.mkdir(sourcedir) end -- save original file path package:originfile_set(path.absolute(packagefile)) end) ``` Custom download requires the user to fully control the download logic, which will be more complicated, and is not recommended unless necessary. If you just want to add custom http headers to obtain download authorization, you can see [Set http headers when downloading package](/api/description/builtin-policies#package-download-http-headers). ### Platform Filtering The complete filtering syntax is as follows: `plat|arch1,arch2@host|arch1,arch2` It looks very complicated, but it is very simple. Each stage is optional and can be partially omitted. Corresponding: \`Compile Platform|Compile Architecture@Host Platform|Host Architecture If you do not set any platform filtering conditions, then the default full platform support, the script inside is effective for all platforms, for example: ```lua on_install(function (package)     -- TODO end) ``` If the installation script is valid for a specific platform, then directly specify the corresponding compilation platform, you can specify more than one at the same time: ```lua on_install("linux", "macosx", function (package)     -- TODO end) ``` If you want to break down to the specified architecture to take effect, you can write: ```lua on_install("linux|x86_64", "iphoneos|arm64", function (package)     -- TODO end) ``` If you want to limit the execution of the host environment platform and architecture, you can append `@host|arch` to the end, for example: ```lua on_install("mingw@windows", function (package)     -- TODO end) ``` This means that only the mingw platform is valid for Windows. We can also specify the host platform and architecture without specifying a platform and architecture. This is usually used to describe some dependencies related to the build tool and can only be run in the host environment. For example, the package we compiled depends on cmake, we need to add the package description of cmake, then the compiler installation environment can only be the host platform: ```lua on_install("@windows", "@linux", "@macosx", function (package)     -- TODO end) ``` Some other examples: ```lua -- `@linux` -- `@linux|x86_64` -- `@macosx,linux` -- `android@macosx, linux` -- `android|armeabi-v7a@macosx,linux` -- `android|armeabi-v7a@macosx,linux|x86_64` -- `android|armeabi-v7a@linux|x86_64` ``` ### Compilation Tools We have built-in scripts for installing common build tools for convenient build support for different source code-dependent build toolchains, such as autoconf, cmake, meson, etc. ### xmake If it is a xmake-based dependency package, then the integration is very simple, xmake has very good built-in integration support, you can directly support it for cross-platform compilation, generally only need to: ```lua on_install(function (package)     import("package.tools.xmake").install(package) end) ``` If you want to pass some unique build configuration parameters: ```lua on_install(function (package)     import("package.tools.xmake").install(package, {"--xxx=y"}) end) ``` ### cmake If it is a cmake-based package, the integration is also very short-answered. Usually you only need to set some configuration parameters, but you need to add the cmake dependency first: ```lua add_deps("cmake") on_install(function (package)     import("package.tools.cmake").install(package, {"-Dxxx=ON"}) end) ``` ### autoconf If it is based on autoconf package, the integration method is similar to cmake, but the configuration parameters are different. However, under normal circumstances, the Unix system has built-in autoconf series tools, so it is fine without any dependencies. ```lua on_install(function (package)     import("package.tools.autoconf").install(package, {"--enable-shared=no"}) end) ``` However, some source packages may not be fully satisfied with the system's built-in autoconf, so you can add autoconf family dependencies and build them: ```lua add_deps("autoconf", "automake", "libtool", "pkg-config") on_install(function (package)     import("package.tools.autoconf").install(package, {"--enable-shared=no"}) end) ``` ### meson If it is meson, you need to add ninja's dependencies to perform the build. ```lua add_deps("meson", "ninja") on_install(function (package)     import("package.tools.meson").install(package, {"-Dxxx=ON"}) end) ``` ### gn If it is a GN project, you can build and install it using the following methods. Make sure to also add ninja as a dependency. ```lua add_deps("gn", "ninja") on_install(function (package) import("package.tools.gn").install(package) end) ``` ### make You can also build and install projects using makefiles. ```lua add_deps("make") on_install(function (package) import("package.tools.make").install(package) end) ``` ### msbuild If the package uses Visual Studio projects you can build them using msbuild. ```lua on_install(function (package) import("package.tools.msbuild").build(package) -- you then have to copy the built binaries manually end) ``` ### ninja You can also build and install packages with ninja. ```lua add_deps("ninja") on_install(function (package) import("package.tools.ninja").install(package) end) ``` ### nmake You can build and install packages with nmake ```lua on_install(function (package) import("package.tools.nmake").install(package) end) ``` ### scons You can build packages using scons. ```lua add_deps("scons") on_install(function (package) import("package.tools.scons").build(package) -- you then need to manually copy the built binaries end) ``` ## on\_test * Test package #### Function Prototype ::: tip API ```lua on_test(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Test script function with package parameter | #### Usage After installation, you need to set the corresponding test script, perform some tests to ensure the reliability of the installation package, if the test does not pass, the entire installation package will be revoked. ```lua on_test(function (package)     assert(package:has_cfuncs("inflate", {includes = "zlib.h"})) end) ``` The above script calls the built-in `has_cfuncs` interface to detect whether the zlib.h header file exists in the installed package, and whether the interface function `inflate` exists in the library and header files. Xmake will try to compile the link for testing, `has_cfuncs` for detecting c functions, and `has_cxxfuncs` for detecting c++ library functions. And include multiple header files in include, for example: `includes = {"xxx.h", "yyy.h"}` We can also pass some of our own compilation parameters into the detection, for example: ```lua on_test(function (package)     assert(package:has_cxxfuncs("func1", {includes = "xxx.h", configs = {defines = "c++14", cxflags = "-Dxxx"}})) end) ``` We can also detect a code snippet with `check_csnippets` and `check_cxxsnippets`: ```lua on_test(function (package) assert(package:check_cxxsnippets({test = [[ #include #include #include #include using namespace boost::algorithm; using namespace std; static void test() { string str("a,b"); vector strVec; split(strVec, str, is_any_of(",")); assert(strVec.size()==2); assert(strVec[0]=="a"); assert(strVec[1]=="b"); } ]]}, {configs = {languages = "c++14"}})) end) ``` if it is an executable package, it can also be detected by trying to run: ```lua on_test(function (package)     os.run("xxx --help") end) ``` if the run fails, the test will not pass. ## on\_component * Define package component #### Function Prototype ::: tip API ```lua on_component(component: , script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | component | Component name string, optional (if not provided, applies to all components) | | script | Component configuration script function with package and component parameters | #### Usage This is a new interface added in 2.7.3 to support component-based configuration of packages, see: [#2636](https://github.com/xmake-io/xmake/issues/2636) for details. Through this interface we can configure the current package, specifying component details such as links to components, dependencies etc. ### Configuring component link information ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-graphics" ... e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("links", "freetype") component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) on_component("window", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-window" ... e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) ... ``` On the user side, we can use package specific components in the following way. ```lua add_requires("sfml") target("test") add_packages("sfml", {components = "graphics") ``` ::: tip NOTE In addition to configuring the component information, we also need to configure the list of available components in order to use it properly, so it is usually used in conjunction with the `add_components` interface. ::: A full example of the configuration and use of package components can be found at: [components example](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua) ### Configuring compilation information for components We can configure not only the linking information for each component, but also the compilation information for includedirs, defines etc. We can also configure each component individually. ```lua package("sfml") on_component("graphics", function (package, component) package:add("defines", "TEST") end) ``` ### Configuring component dependencies ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) component:add("deps", "window", "system") end) ``` The above configuration tells the package that our graphics component will have additional dependencies on the `window` and `system` components. So, on the user side, our use of the graphics component can be done from the ```lua add_packages("sfml", {components = {"graphics", "window", "system"}) ``` Simplified to. ```lua add_packages("sfml", {components = "graphics") ``` Because, as soon as we turn on the graphics component, it will also automatically enable the dependent window and system components. Alternatively, we can configure component dependencies with `add_components("graphics", {deps = {"window", "system"}})`. ### Finding components from the system library We know that configuring `add_extsources` in the package configuration can improve package discovery on the system, for example by finding libraries from system package managers such as apt/pacman. Of course, we can also make it possible for each component to prioritise finding them from the system repositories via the `extsources` configuration as well. For example, the sfml package, which is actually also componentized in homebrew, can be made to find each component from the system repository without having to install them in source each time. ```sh $ ls -l /usr/local/opt/sfml/lib/pkgconfig -r--r--r-- 1 ruki admin 317 10 19 17:52 sfml-all.pc -r--r--r-- 1 ruki admin 534 10 19 17:52 sfml-audio.pc -r--r--r-- 1 ruki admin 609 10 19 17:52 sfml-graphics.pc -r--r--r-- 1 ruki admin 327 10 19 17:52 sfml-network.pc -r--r--r-- 1 ruki admin 302 10 19 17:52 sfml-system.pc -r--r--r-- 1 ruki admin 562 10 19 17:52 sfml-window.pc ``` We just need, for each component, to configure its extsources: the ```lua if is_plat("macosx") then add_extsources("brew::sfml/sfml-all") end on_component("graphics", function (package, component) -- ... component:add("extsources", "brew::sfml/sfml-graphics") end) ``` ### Default global component configuration In addition to configuring specific components by specifying component names, if we don't specify a component name, the default is to globally configure all components. ```lua package("sfml") on_component(function (package, component) -- configure all components end) ``` Of course, we could also specify the configuration of the graphics component and the rest of the components would be configured via the default global configuration interface in the following way. ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) -- configure graphics end) on_component(function (package, component) -- component audio, network, window, system end) ``` --- --- url: /guide/package-management/package-distribution.md --- # Package Distribution ## Define package configuration ### Package structure in repository Before making our own package, we need to understand the structure of the package repository. Whether it is the official package repository or a self-built private package repository, the structure is the same: ``` xmake-repo - packages - t/tbox/xmake.lua - z/zlib/xmake.lua ``` Through the above structure, you can see that each package will have an xmake.lua to describe its installation rules, and according to the `z/zlib` two-level sub-category storage, it is convenient for quick retrieval. ### Package Description The description rules for the package are basically done in its xmake.lua, which is similar to the xmake.lua description in the project. The difference is that the description field only supports `package()`. However, in the project xmake.lua, you can also directly add `package()` to the built-in package description, and even the package repository is saved, which is sometimes more convenient. First, let's take a look at zlib's description rules first. This rule can be found at [xmake-repo/z/zlib/xmake.lua](https://github.com/xmake-io/xmake-repo/blob/master/packages/z/zlib/xmake.lua). ``` package("zlib") set_homepage("http://www.zlib.net") set_description("A Massively Spiffy Yet Delicately Unobtrusive Compression Library") set_urls("http://zlib.net/zlib-$(version).tar.gz", "https://downloads.sourceforge.net/project/libpng/zlib/$(version)/zlib-$(version).tar.gz") add_versions("1.2.10", "8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017") add_versions("1.2.11", "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1") on_install("windows", function (package) io.gsub("win32/Makefile.msc", "%-MD", "-" .. package:config("vs_runtime")) os.vrun("nmake -f win32\\Makefile.msc zlib.lib") os.cp("zlib.lib", package:installdir("lib")) os.cp("*.h", package:installdir("include")) end) on_install("linux", "macosx", function (package) import("package.tools.autoconf").install(package, {"--static"}) end) on_install("iphoneos", "android@linux,macosx", "mingw@linux,macosx", function (package) import("package.tools.autoconf").configure(package, {host = "", "--static"}) io.gsub("Makefile", "\nAR=.-\n", "\nAR=" .. (package:build_getenv("ar") or "") .. "\n") io.gsub("Makefile", "\nARFLAGS=.-\n", "\nARFLAGS=cr\n") io.gsub("Makefile", "\nRANLIB=.-\n", "\nRANLIB=\n") os.vrun("make install -j4") end) on_test(function (package) assert(package:has_cfuncs("inflate", {includes = "zlib.h"})) end) ``` This package rule adds installation rules to Windows, Linux, macOS, iPhoneOS, MinGW, and other platforms. Basically, it has achieved full platform coverage, and even some cross-compilation platforms, which is a typical example. Of course, some packages rely on source code implementation and are not completely cross-platform, so you only need to set the installation rules for the platforms it supports. For more detailed package configuration API descriptions, see: [Package Interface Documentation](/api/description/package-dependencies) ### Extended configuration parameters See: [add\_configs](/api/description/package-dependencies#add-configs) for details. ### Built-in configuration parameters In addition to setting some extended configuration parameters via [add\_configs](/api/description/package-dependencies#add-configs), xmake also provides some built-in configuration parameters that can be used. #### Enable debug package ```lua add_requires("xxx", {debug = true}) ``` There must be relevant processing in the package description to support: ```lua on_install(function (package) Local configs = {} if package:is_debug() then table.insert(configs, "--enable-debug") end import("package.tools.autoconf").install(package) end) ``` #### Setting up the msvc runtime library ```lua add_requires("xxx", {configs = {vs_runtime = "MT"}}) ``` Normally, packages installed by built-in tool scripts such as `import("package.tools.autoconf").install` are automatically processed internally by vs\_runtime. But if it is a special source package, the build rules are special, then you need to handle it yourself: ```lua on_install(function (package) io.gsub("build/Makefile.win32.common", "%-MD", "-" .. package:config("vs_runtime")) end) ``` ### Adding environment variables For some libraries, there are also executable tools. If you need to use these tools in the integration package, you can also set the corresponding PATH environment variable: ```lua package("luajit") on_load(function (package) if is_plat("windows") then Package:addenv("PATH", "lib") end Package:addenv("PATH", "bin") end) ``` In the project, the corresponding environment variables will only take effect after the corresponding package is integrated by `add_packages`. ```lua add_requires("luajit") target("test") set_kind("binary") add_packages("luajit") after_run(function (package) os.exec("luajit --version") end) ``` ### Installing binary packages Xmake also supports direct reference to the binary version package, which is used directly for installation, for example: ```lua if is_plat("windows") then set_urls("https://www.libsdl.org/release/SDL2-devel-$(version)-VC.zip") add_versions("2.0.8", "68505e1f7c16d8538e116405411205355a029dcf2df738dbbc768b2fe95d20fd") end on_install("windows", function (package) os.cp("include", package:installdir()) os.cp("lib/$(arch)/*.lib", package:installdir("lib")) os.cp("lib/$(arch)/*.dll", package:installdir("lib")) end) ``` ### Local test If you have added and created a new package in the local xmake-repo repository, you can run the test locally and pass it. If the test passes, you can submit the PR to the official repository and request the merge. We can execute the following script to test the specified package: ```sh cd xmake-repo xmake l scripts/test.lua -v -D zlib ``` The above command will force the download and installation of the zlib package to test whether the entire installation process is ok. Adding `-v -D` is to see the complete detailed log information and error information, which is convenient for debugging analysis. If the network environment is not good, and you do not want to re-download all dependencies every time, you can add the `--shallow` parameter to execute. This parameter tells the script to just re-decompress the local cached zlib source package, re-execute the installation command, but will not download various dependencies. ```sh cd xmake-repo xmake l scripts/test.lua -v -D --shallow zlib ``` If we want to test the package rules of other platforms, such as: Android, iPhoneOS, and other platforms, you can specify by `-p/--plat` or `-a/--arch`. ```sh cd xmake-repo xmake l scripts/test.lua -v -D --shallow -p iphoneos -a arm64 zlib xmake l scripts/test.lua -v -D --shallow -p android --ndk=/xxxx zlib ``` ## Generate package configuration We can also run `xmake package` to generate a remote package configuration. ```sh $ xmake package -f remote ``` ```lua [packages/f/foo/xmake.lua] package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") add_urls("https://github.com/myrepo/foo.git") add_versions("1.0", "") on_install(function (package) local configs = {} if package:config("shared") then configs.kind = "shared" end import("package.tools.xmake").install(package, configs) end) on_test(function (package) - TODO check includes and interfaces - assert(package:has_cfuncs("foo", {includes = "foo.h"}) end) ``` Compared with the local package, the package definition configuration has more actual installation logic, as well as the settings of urls and versions, We can also modify urls, versions and other configuration values through additional parameters, for example: ```sh $ xmake package -f remote --url=https://xxxx/xxx.tar.gz --shasum=xxxxx --homepage=xxxxx` ``` xmake will also read the relevant configuration information from the target's `set_license` and `set_version` configurations. ## Submit packages to the official repository If you need a package that is not supported by the current official repository, you can commit it to the official repository after local tuning: [xmake-repo](https://github.com/xmake-io/xmake-repo) For detailed contribution descriptions, see: [CONTRIBUTING.md](https://github.com/xmake-io/xmake-repo/blob/master/CONTRIBUTING.md) For how to make and use your self-built private package, you can read this: [Using self-built private package repository ](/guide/package-management/using-official-packages.html#using-self-built-private-package-repository). ## Distributing and using custom package rules Since version 2.7.2 we have been able to add custom build rule scripts to the package management repository to enable dynamic distribution and installation following packages. We need to place the custom rules in the `packages/x/xxx/rules` directory of the repository and it will follow the package as it is installed. But it has also some limits: * For in-package rules, we cannot add `on_load`, `after_load` scripts, but we can usually use `on_config` instead. ### Adding package rules We need to add the rules script to the rules fixed directory, for example: packages/z/zlib/rules/foo.lua ```lua rule("foo") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` ### Applying package rules The rules are used in a similar way as before, the only difference being that we need to specify which package's rules to access by prefixing them with `@packagename/`. The exact format: `add_rules("@packagename/rulename")`, e.g.: `add_rules("@zlib/foo")`. ```lua add_requires("zlib", {system = false}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib") add_rules("@zlib/foo") ``` ### Referencing rules by package alias If a package alias exists, xmake will give preference to the package alias to get the rules. ```lua add_requires("zlib", {alias = "zlib2", system = false}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib2") add_rules("@zlib2/foo") ``` ### Adding package rule dependencies We can use `add_deps("@bar")` to add additional rules relative to the current package directory. However, we cannot add rule dependencies from other packages, they are completely isolated and we can only refer to rules from other packages imported by `add_requires` in the user project. packages/z/zlib/rules/foo.lua ```lua rule("foo") add_deps("@bar") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` packages/z/zlib/rules/bar.lua ```lua rule("bar") on_config(function (target) print("bar: on_config %s", target:name()) end) ``` --- --- url: /api/scripts/package-instance.md --- # Package Instance This page describes the interface for `package` of functions like `on_load()`, `on_install()` or `on_test()` of the [Package Dependencies](/api/description/package-dependencies) ## package:name * Get the name of the package #### Function Prototype ::: tip API ```lua package:name() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:get * Get the values of the package by name #### Function Prototype ::: tip API ```lua package:get(key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | #### Usage ```lua -- get the dependencies package:get("deps") -- get the links package:get("links") -- get the defined macros package:get("defines") ``` ## package:set * Set the values of the package by name #### Function Prototype ::: tip API ```lua package:set(key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | | value | Configuration value | #### Usage If you just want to add values use [package:add](#package-add) ```lua -- set the dependencies package:set("deps", "python") -- set the links package:set("links", "sdl2") -- set the defined macros package:set("defines", "SDL_MAIN_HANDLED") ``` ::: tip NOTE Any script scope configuration using `package:set("xxx", ...)` is completely consistent with the corresponding `set_xxx` interface in the description scope. For specific parameter descriptions, you can directly refer to the corresponding `set_xxx` interface documentation in the description scope. For example: * Description scope: `set_urls("https://github.com/madler/zlib/archive/$(version).tar.gz")` * Script scope: `package:set("urls", "https://github.com/madler/zlib/archive/$(version).tar.gz")` ::: ## package:add * Add to the values of the package by name #### Function Prototype ::: tip API ```lua package:add(key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | | value | Value to add | #### Usage ```lua -- add dependencies package:add("deps", "python") -- add links package:add("links", "sdl2") -- add defined macros package:add("defines", "SDL_MAIN_HANDLED") ``` ::: tip NOTE Any script scope configuration using `package:add("xxx", ...)` is completely consistent with the corresponding `add_xxx` interface in the description scope. For specific parameter descriptions, you can directly refer to the corresponding `add_xxx` interface documentation in the description scope. For example: * Description scope: `add_deps("zlib", {configs = {shared = true}})` * Script scope: `package:add("deps", "zlib", {configs = {shared = true}})` ::: ## package:license * Get the license of the package #### Function Prototype ::: tip API ```lua package:license() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Same as `package:get("license")` ## package:description * Get the description of the package #### Function Prototype ::: tip API ```lua package:description() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Same as `package:get("description")` ## package:plat * Get the platform of the package. Can be any of: #### Function Prototype ::: tip API ```lua package:plat() ``` ::: #### Parameter Description No parameters required for this function. #### Usage * windows * linux * macosx * android * iphoneos * watchos * mingw * cygwin * bsd If the package is binary [`os.host`](/api/scripts/builtin-modules/os#os-host) is returned ## package:arch * Get the architecture of the package (e.g. x86, x64, x86\_64) #### Function Prototype ::: tip API ```lua package:arch() ``` ::: #### Parameter Description No parameters required for this function. #### Usage If the package is binary [`os.arch`](/api/scripts/builtin-modules/os#os-arch) is returned ## package:targetos * Get the targeted OS of the package. #### Function Prototype ::: tip API ```lua package:targetos() ``` ::: #### Parameter Description No parameters required for this function. #### Usage There are the same values as [package:plat](#package-plat) ## package:targetarch * Get the targeted architecture of the package. #### Function Prototype ::: tip API ```lua package:targetarch() ``` ::: #### Parameter Description No parameters required for this function. #### Usage There are the same values as [package:arch](#package-arch) ## package:is\_plat * Wether the current platform is one of the given platforms #### Function Prototype ::: tip API ```lua package:is_plat(plat: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | plat | Platform name | #### Usage ```lua -- Is the current platform android? package:is_plat("android") -- Is the current platform windows, linux or macosx? package:is_plat("windows", "linux", "macosx") ``` ## package:is\_arch * Wether the current platform is one of the given platforms #### Function Prototype ::: tip API ```lua package:is_arch(arch: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | arch | Architecture name | #### Usage ```lua -- Is the current architecture x86 package:is_arch("x86") -- Is the current architecture x64 or x86_64 package:is_arch("x64", "x86_64") ``` ## package:is\_targetos * Wether the currently targeted OS is one of the given OS #### Function Prototype ::: tip API ```lua package:is_targetos() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- Is the currently targeted OS windows? package:is_targetos("windows") -- Is the currently targeted OS android or iphoneos? package:is_targetos("android", "iphoneos") ``` ## package:is\_targetarch * Wether the currently targeted architecture is one of the given architectures #### Function Prototype ::: tip API ```lua package:is_targetarch() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- Is the currently targeted architecture x86 package:is_targetarch("x86") -- Is the currently targeted architecture x64 or x86_64 package:is_targetarch("x64", "x86_64") ``` ## package:alias * Get the alias of the package #### Function Prototype ::: tip API ```lua package:alias() ``` ::: #### Parameter Description No parameters required for this function. #### Usage If the user sets an alias like so: ```lua add_requires("libsdl", {alias = "sdl"}) ``` This alias can be retrieved by ```lua -- returns "sdl" package:alias() ``` ## package:urls * Get the URLs of the package #### Function Prototype ::: tip API ```lua package:urls() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Retrieve the URLs set by: ```lua add_urls("https://example.com/library-$(version).zip") -- or so set_urls("https://example.com/library-$(version).zip") ``` Then write this: ```lua -- returns the table {"https://example.com/library-$(version).zip"} package:urls() ``` ## package:dep * Get a dependency of the package by name. The name needs to be a dependency of the package. #### Function Prototype ::: tip API ```lua package:dep(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Dependency name | #### Usage ```lua local python = package:dep("python") -- returns "python" python:name() ``` ## package:deps * Get all dependencies of the package #### Function Prototype ::: tip API ```lua package:deps() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- prints the names of all dependencies for _,dep in pairs(package:deps()) do print(dep:name()) end ``` ## package:sourcehash * Get the sha256 checksum of an URL alias #### Function Prototype ::: tip API ```lua package:sourcehash() ``` ::: #### Parameter Description No parameters required for this function. #### Usage If the checksum is provided like so: ```lua add_urls("https://example.com/library-$(version).zip", {alias = "example"}) add_versions("example:2.4.1", "29f9983cc7196e882c4bc3d23d7492f9c47574c7cf658afafe7d00c185429941") ``` You can retrieve the checksum like so: ```lua -- returns "29f9983cc7196e882c4bc3d23d7492f9c47574c7cf658afafe7d00c185429941" package:sourcehash("example") -- or so package:sourcehash(package:url_alias(package:urls()[1])) ``` ## package:kind * Get the kind of the package. Can be any of: #### Function Prototype ::: tip API ```lua package:kind() ``` ::: #### Parameter Description No parameters required for this function. #### Usage * binary * toolchain (is also binary) * library (default) * template [#2138](https://github.com/xmake-io/xmake/issues/2138) * headeronly ## package:is\_binary * Wether the package is of kind binary #### Function Prototype ::: tip API ```lua package:is_binary() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:is\_toolchain * Wether the package is of kind toolchain #### Function Prototype ::: tip API ```lua package:is_toolchain() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:is\_library * Wether the package is of kind library #### Function Prototype ::: tip API ```lua package:is_library() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:is\_toplevel * Wether the package is directly required by the user (e.g. xmake.lua) #### Function Prototype ::: tip API ```lua package:is_toplevel() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:is\_thirdparty * Wether the package is provided by a thirdparty package manager (e.g. brew, conan, vcpkg) #### Function Prototype ::: tip API ```lua package:is_thirdparty() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:is\_debug * Wether the package is build with debug mode (Same as `package:config("debug")`) #### Function Prototype ::: tip API ```lua package:is_debug() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:is\_supported * Wether the package is supported by the current platform and architecture #### Function Prototype ::: tip API ```lua package:is_supported() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:debug * Wether the the package gets built with debug mode #### Function Prototype ::: tip API ```lua package:debug() ``` ::: #### Parameter Description No parameters required for this function. #### Usage It's deprecated, please use [`package:is_debug`](#package-is_debug) instead ## package:is\_cross * Wether the package is getting cross-compiled #### Function Prototype ::: tip API ```lua package:is_cross() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:cachedir * Get the cache directory of the package #### Function Prototype ::: tip API ```lua package:cachedir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:installdir * Get the installation directory of the package. Can also be used to get a subdirectory. If the given directory tree does not exist it will be created. #### Function Prototype ::: tip API ```lua package:installdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- returns the installation directory package:installdir() -- returns the subdirectory include inside the installation directory package:installdir("include") -- returns the subdirectory include/files package:installdir("include", "files") ``` ## package:scriptdir * Get the directory where the xmake.lua of the package lies #### Function Prototype ::: tip API ```lua package:scriptdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:envs * Get the exported environment variables of the package #### Function Prototype ::: tip API ```lua package:envs() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:getenv * Get the given environment variable #### Function Prototype ::: tip API ```lua package:getenv() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- returns a table package:getenv("PATH") ``` ## package:setenv * Set the given environment variable. Overwrites the variable #### Function Prototype ::: tip API ```lua package:setenv() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- sets PATH to {"bin", "lib"} package:setenv("PATH", "bin", "lib") ``` ## package:addenv * Add the given values to the environment variable #### Function Prototype ::: tip API ```lua package:addenv() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- adds "bin" and "lib" to PATH package:addenv("PATH", "bin", "lib") ``` ## package:versions * Get all version strings of the package. Returns a table containing all versions as strings #### Function Prototype ::: tip API ```lua package:versions() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:version * Get the version of the package #### Function Prototype ::: tip API ```lua package:version() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua local version = package:version() -- get the major version version:major() -- get the minor version version:minor() -- get the patch version version:patch() ``` ## package:version\_str * Get the version of the package as string #### Function Prototype ::: tip API ```lua package:version_str() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:config * Get the given configuration value of the package #### Function Prototype ::: tip API ```lua package:config() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- if configurations are set like so add_require("example", {configs = {enable_x = true, value_y = 6}}) -- these values can be retrieved like so -- returns true package:config("enable_x") -- returns 6 package:config("value_y") ``` ## package:config\_set * Set the given configuration value of the package #### Function Prototype ::: tip API ```lua package:config_set() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua package:config_set("enable_x", true) package:config_set("value_y", 6) ``` ## package:configs * Get all configurations of the package #### Function Prototype ::: tip API ```lua package:configs() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- returns a table with the configuration names as keys and their values as values local configs = package:configs() local enable_x = configs["enable_x"] local value_y = configs["value_y"] ``` ## package:buildhash * Get the build hash of the package #### Function Prototype ::: tip API ```lua package:buildhash() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## package:patches * Get all patches of the current version #### Function Prototype ::: tip API ```lua package:patches() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua -- returns a table with all patches local patches = package:patches() -- each element contains the keys "url" and "sha256" local url = patches[1]["url"] local sha256 = patches[1]["sha256"] ``` ## package:has\_cfuncs * Wether the package has the given C functions #### Function Prototype ::: tip API ```lua package:has_cfuncs() ``` ::: #### Parameter Description No parameters required for this function. #### Usage This should be used inside `on_test` like so: ```lua on_test(function (package) assert(package:has_cfuncs("foo")) -- you can also add configs assert(package:has_cfuncs("bar", {includes = "foo_bar.h"})) assert(package:has_cfuncs("blob", {includes = "blob.h", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_cfuncs("bla", {configs = {languages = "c99"}})) end) ``` ## package:has\_cxxfuncs * Wether the package has the given C++ functions #### Function Prototype ::: tip API ```lua package:has_cxxfuncs(funcs: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | funcs | Function name or function name list | #### Usage This should be used inside `on_test` like so: ```lua on_test(function (package) assert(package:has_cxxfuncs("foo")) -- you can also add configs assert(package:has_cxxfuncs("bar", {includes = "foo_bar.hpp"})) assert(package:has_cxxfuncs("blob", {includes = "blob.hpp", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_cxxfuncs("bla", {configs = {languages = "cxx17"}})) end) ``` ## package:has\_ctypes * Wether the package has the given C types #### Function Prototype ::: tip API ```lua package:has_ctypes(types: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | Type name or type name list | #### Usage This should be used inside `on_test` like so: ```lua on_test(function (package) assert(package:has_ctypes("foo")) -- you can also add configs assert(package:has_ctypes("bar", {includes = "foo_bar.h"})) assert(package:has_ctypes("blob", {includes = "blob.h", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_ctypes("bla", {configs = {languages = "c99"}})) end) ``` ## package:has\_cxxtypes * Wether the package has the given C++ types #### Function Prototype ::: tip API ```lua package:has_cxxtypes(types: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | Type name or type name list | #### Usage This should be used inside `on_test` like so: ```lua on_test(function (package) assert(package:has_cxxtypes("foo")) -- you can also add configs assert(package:has_cxxtypes("bar", {includes = "foo_bar.hpp"})) assert(package:has_cxxtypes("blob", {includes = "blob.hpp", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_cxxtypes("bla", {configs = {languages = "cxx17"}})) end) ``` ## package:has\_cincludes * Wether the package has the given C header files #### Function Prototype ::: tip API ```lua package:has_cincludes(includes: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Include file or include file list | #### Usage This should be used in `on_test` like so: ```lua on_test(function (package) assert(package:has_cincludes("foo.h")) end) ``` ## package:has\_cxxincludes * Wether the package has the given C++ header files #### Function Prototype ::: tip API ```lua package:has_cxxincludes(includes: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Include file or include file list | #### Usage This should be used in `on_test` like so: ```lua on_test(function (package) assert(package:has_cxxincludes("foo.hpp")) end) ``` ## package:check\_csnippets * Wether the given C snippet can be compiled and linked #### Function Prototype ::: tip API ```lua package:check_csnippets(snippets: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | snippets | Code snippet or code snippet list | #### Usage This should be used in `on_test` like so: ```lua on_test(function (package) assert(package:check_csnippets({test = [[ #define USE_BLOB #include void test(int argc, char** argv) { foo bar; printf("%s", bar.blob); } ]]}, {configs = {languages = "c99"}, includes = "foo.h"})) end) ``` ## package:check\_cxxsnippets * Wether the given C++ snippet can be compiled and linked #### Function Prototype ::: tip API ```lua package:check_cxxsnippets(snippets: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | snippets | Code snippet or code snippet list | #### Usage This should be used in `on_test` like so: ```lua on_test(function (package) assert(package:check_cxxsnippets({test = [[ #define USE_BLOB #include void test(int argc, char** argv) { foo bar(); std::cout << bar.blob; } ]]}, {configs = {languages = "cxx11"}, includes = "foo.hpp"})) end) ``` ## package:check\_fcsnippets * Wether the given Fortran snippet can be compiled and linked #### Function Prototype ::: tip API ```lua package:check_fcsnippets() ``` ::: #### Parameter Description No parameters required for this function. #### Usage see above --- --- url: /guide/package-management/package-management-in-project.md --- # Package Management in Project The package management command `$ xmake require` can be used to manually display, download, install, uninstall, retrieve, and view package information. `xmake require` is only used for the current project. We also provide a more convenient, independent `xrepo` package manager command to install, uninstall, find, and manage packages globally. For detailed documentation, see: [Getting Started with Xrepo Commands](/guide/package-management/xrepo-cli) ## Install the specified package ```sh $ xmake require tbox ``` Install the specified version package: ```sh $ xmake require tbox "~1.6" ``` Force a re-download of the installation and display detailed installation information: ```sh $ xmake require -f -v tbox "1.5.x" ``` Pass additional setup information: ```sh $ xmake require --extra="{debug=true,config={small=true}}" tbox ``` Install the debug package and pass the compilation configuration information of `small=true` to the package. ## Uninstall the specified package ```sh $ xmake require --uninstall tbox ``` This will completely uninstall and remove the package file. ## Show package information ```sh $ xmake require --info tbox ``` ## Search for packages in the current repository ```sh $ xmake require --search tbox ``` This supports fuzzy search and Lua pattern matching search: ```sh $ xmake require --search pcr ``` Will also search for pcre, pcre2, and other packages. ## List the currently installed packages ```sh $ xmake require --list ``` --- --- url: /posts/package-target.md --- Packages all targets for the current platform: ```bash $xmake p $xmake package ``` Packages the target test to the output directory: /tmp ```bash $xmake p -o /tmp test $xmake p --output=/tmp test ``` Packages targets for the iphoneos platform. ```bash $xmake f -p iphoneos $xmake p ``` We can uses the macro plugin to package all architectures of the given platform. ```bash # packages targets for all architectures of the current platform $xmake macro package # packages targets for all architectures of the iphoneos platform $xmake m package -p iphoneos # packages targets with debug version for all architectures of the iphoneos platform and output to the directory: /tmp/output $xmake m package -p iphoneos -f "-m debug" -o /tmp/output ``` --- --- url: /api/scripts/extension-modules/package/tools.md --- # package.tools This module provides helpers for integrating common build tools in xmake-repo and custom package scripts. Each tool module (cmake, autoconf, meson, make, ninja, msbuild, xmake) provides a set of APIs for building and installing packages. Below are detailed API references and usage examples for each tool. ## cmake > Import: `import("package.tools.cmake")` ### cmake.install Install a package using CMake. Most commonly used for CMake-based packages in xmake-repo. #### Function Prototype ::: tip API ```lua cmake.install(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. CMake configuration parameter list | | opt | Optional. Option parameters, supports the following:- `cxflags` - C/C++ compile flags- `cflags` - C compile flags- `packagedeps` - Package dependencies list | #### Return Value No return value #### Usage #### Basic usage ```lua on_install(function (package) local configs = {} table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) import("package.tools.cmake").install(package, configs) end) ``` * Use `package:config("shared")` to control shared/static build. * Use `package:is_debug()` to control Debug/Release build. #### Passing custom compile flags (cxflags/cflags) ```lua on_install(function (package) local configs = {} table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) local cxflags = "-Wa,-mbig-obj" import("package.tools.cmake").install(package, configs, {cxflags = cxflags}) end) ``` * To pass custom C/C++ compile flags, set `cflags` or `cxflags` as a variable and pass it via the `opt` table to `cmake.install`. * This is useful for special compiler options or platform-specific flags. ### cmake.build Build a package using CMake (without install step). #### Function Prototype ::: tip API ```lua cmake.build(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. CMake configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.cmake").build(package, {"-DCMAKE_BUILD_TYPE=Release"}) end) ``` ### cmake.configure Configure a CMake project (run cmake only, no build/install). #### Function Prototype ::: tip API ```lua cmake.configure(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. CMake configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage #### Basic usage ```lua on_install(function (package) local configs = {} table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) import("package.tools.cmake").configure(package, configs) end) ``` ## autoconf > Import: `import("package.tools.autoconf")` ### autoconf.install Install a package using GNU Autotools (configure/make/make install). #### Function Prototype ::: tip API ```lua autoconf.install(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Configuration parameter list | | opt | Optional. Option parameters, supports the following:- `packagedeps` - Package dependencies list | #### Return Value No return value #### Usage #### Basic usage ```lua on_install(function (package) local configs = {} table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) import("package.tools.autoconf").install(package, configs) end) ``` * Use `--enable-static` and `--enable-shared` to control static/shared build. See [libx11 example](https://raw.githubusercontent.com/xmake-io/xmake-repo/refs/heads/dev/packages/l/libx11/xmake.lua). #### Advanced usage ```lua on_install(function (package) local configs = {} table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) if package:is_debug() then table.insert(configs, "--enable-debug") end local packagedeps = {"zlib"} import("package.tools.autoconf").install(package, configs, {packagedeps = packagedeps}) end) ``` ### autoconf.build Build a package using Autotools (configure/make). #### Function Prototype ::: tip API ```lua autoconf.build(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.autoconf").build(package, {"--enable-static=yes"}) end) ``` ### autoconf.configure Run the configure script for an Autotools project. #### Function Prototype ::: tip API ```lua autoconf.configure(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.autoconf").configure(package, {"--prefix=/usr/local"}) end) ``` ### autoconf.make Run make with custom arguments. #### Function Prototype ::: tip API ```lua autoconf.make(package: , argv:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | argv | Required. make argument list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.autoconf").make(package, {"install"}) end) ``` ## meson > Import: `import("package.tools.meson")` ### meson.install Install a package using Meson (setup/compile/install). #### Function Prototype ::: tip API ```lua meson.install(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Meson configuration parameter list | | opt | Optional. Option parameters, supports the following:- `packagedeps` - Package dependencies list | #### Return Value No return value #### Usage Typical usage: ```lua add_deps("meson", "ninja") on_install(function (package) local configs = {} table.insert(configs, "-Ddefault_library=" .. (package:config("shared") and "shared" or "static")) if package:is_debug() then table.insert(configs, "-Dbuildtype=debug") else table.insert(configs, "-Dbuildtype=release") end local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.meson").install(package, configs, {packagedeps = packagedeps}) end) ``` ### meson.build Build a package using Meson (setup/compile). #### Function Prototype ::: tip API ```lua meson.build(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Meson configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.meson").build(package, {"-Ddefault_library=static"}) end) ``` ### meson.generate Generate Meson build files only (setup). #### Function Prototype ::: tip API ```lua meson.generate(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Meson configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.meson").generate(package, {"-Dbuildtype=release"}) end) ``` ## make > Import: `import("package.tools.make")` ### make.install Build and install a package using Make (build + install). #### Function Prototype ::: tip API ```lua make.install(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Configuration parameter list | | opt | Optional. Option parameters, supports the following:- `packagedeps` - Package dependencies list | #### Return Value No return value #### Usage Typical usage: ```lua add_deps("make") on_install(function (package) local configs = {} local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.make").install(package, configs, {packagedeps = packagedeps}) end) ``` ### make.build Build a package using Make. #### Function Prototype ::: tip API ```lua make.build(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.make").build(package, {"CC=gcc"}) end) ``` ### make.make Run make with custom arguments. #### Function Prototype ::: tip API ```lua make.make(package: , argv:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | argv | Required. make argument list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.make").make(package, {"install"}) end) ``` ## ninja > Import: `import("package.tools.ninja")` ### ninja.install Build and install a package using Ninja (build + install). #### Function Prototype ::: tip API ```lua ninja.install(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Configuration parameter list | | opt | Optional. Option parameters, supports the following:- `packagedeps` - Package dependencies list | #### Return Value No return value #### Usage Typical usage: ```lua add_deps("ninja") on_install(function (package) local configs = {} local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.ninja").install(package, configs, {packagedeps = packagedeps}) end) ``` ### ninja.build Build a package using Ninja. #### Function Prototype ::: tip API ```lua ninja.build(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. Configuration parameter list | | opt | Optional. Option parameters | #### Return Value No return value #### Usage Example: ```lua on_install(function (package) import("package.tools.ninja").build(package) end) ``` ## msbuild > Import: `import("package.tools.msbuild")` ### msbuild.build Build a package using MSBuild (Visual Studio projects). #### Function Prototype ::: tip API ```lua msbuild.build(package: , configs:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | configs | Required. MSBuild configuration parameter list | | opt | Optional. Option parameters, supports the following:- `packagedeps` - Package dependencies list | #### Return Value No return value #### Usage Typical usage: ```lua on_install(function (package) local configs = {} if package:config("configuration") then table.insert(configs, "/p:Configuration=" .. package:config("configuration")) end local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.msbuild").build(package, configs, {packagedeps = packagedeps}) -- You may need to manually copy built binaries end) ``` ## xmake > Import: `import("package.tools.xmake")` ### xmake.install Install a package using xmake itself. This is suitable for: * Porting third-party libraries that cannot be built directly, by writing a `xmake.lua` to adapt the build. * Building and installing projects that already maintain their own `xmake.lua` build script. #### Function Prototype ::: tip API ```lua xmake.install(package: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | package | Required. Package instance object | | opt | Optional. Option parameters | #### Return Value No return value #### Usage #### Usage for projects with their own xmake.lua If the source already contains a proper `xmake.lua`, you can simply: ```lua on_install(function (package) import("package.tools.xmake").install(package) end) ``` No extra configuration is needed; xmake will handle shared/static/debug/release modes automatically. #### Usage for porting a third-party library If the source does not provide a build system, you can generate a custom `xmake.lua` as shown below: ```lua on_install(function (package) io.writefile("xmake.lua", [[ add_rules("mode.debug", "mode.release") add_requires("libpng") target("bpg") set_kind("static") add_files("libbpg.c") add_files("libavcodec/hevc_cabac.c", "libavcodec/hevc_filter.c", "libavcodec/hevc.c", "libavcodec/hevcpred.c", "libavcodec/hevc_refs.c") add_files("libavcodec/hevcdsp.c", "libavcodec/hevc_mvs.c", "libavcodec/hevc_ps.c", "libavcodec/hevc_sei.c") add_files("libavcodec/utils.c", "libavcodec/cabac.c", "libavcodec/golomb.c", "libavcodec/videodsp.c") add_files("libavutil/mem.c", "libavutil/buffer.c", "libavutil/log2_tab.c", "libavutil/frame.c", "libavutil/pixdesc.c", "libavutil/md5.c") add_includedirs(".") add_headerfiles("libbpg.h") add_defines("HAVE_AV_CONFIG_H", "USE_PRED", "USE_VAR_BIT_DEPTH") on_load(function (target) local version = io.readfile("VERSION") target:add("defines", "CONFIG_BPG_VERSION=" .. version) end) ]]) import("package.tools.xmake").install(package) end) ``` * This approach is useful for adapting third-party sources that lack a proper build system. See the real-world [libbpg package example](https://github.com/xmake-io/xmake-repo/blob/23766d5855508e69f4cb1a4375ab3b865295fcb3/packages/l/libbpg/xmake.lua). ## References * [xmake-repo package scripts](https://github.com/xmake-io/xmake-repo) * [Official guide: Package Distribution](/guide/package-management/package-distribution) --- --- url: /zh/api/scripts/extension-modules/package/tools.md --- # package.tools 此模块为 xmake-repo 及自定义包脚本提供常用构建工具集成辅助。每个工具模块(cmake, autoconf, meson, make, ninja, msbuild, xmake)都提供了一系列用于包构建和安装的 API。下文详细列出各工具的 API 参考与用法示例。 ## cmake > 导入:`import("package.tools.cmake")` ### cmake.install 通过 CMake 安装包,适用于大多数 CMake 类包。 #### 函数原型 ::: tip API ```lua cmake.install(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。CMake 配置参数列表 | | opt | 可选。选项参数,支持以下选项:- `cxflags` - C/C++ 编译参数- `cflags` - C 编译参数- `packagedeps` - 包依赖列表 | #### 返回值说明 无返回值 #### 用法说明 #### 基础用法 ```lua on_install(function (package) local configs = {} table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) import("package.tools.cmake").install(package, configs) end) ``` * 用 `package:config("shared")` 控制动态/静态库。 * 用 `package:is_debug()` 控制 Debug/Release。 #### 传递自定义编译参数(cxflags/cflags) ```lua on_install(function (package) local configs = {} table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) local cxflags = "-Wa,-mbig-obj" import("package.tools.cmake").install(package, configs, {cxflags = cxflags}) end) ``` * 如需传递自定义 C/C++ 编译参数,直接将 `cflags` 或 `cxflags` 作为变量,通过 opt 表传递给 cmake.install。 * 适用于特殊编译选项或平台相关参数。 ### cmake.build 仅用 CMake 构建包(不安装)。 #### 函数原型 ::: tip API ```lua cmake.build(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。CMake 配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.cmake").build(package, {"-DCMAKE_BUILD_TYPE=Release"}) end) ``` ### cmake.configure 仅配置 CMake 项目(只运行 cmake,不编译/安装)。 #### 函数原型 ::: tip API ```lua cmake.configure(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。CMake 配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 #### 基础用法 ```lua on_install(function (package) local configs = {} table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:is_debug() and "Debug" or "Release")) import("package.tools.cmake").configure(package, configs) end) ``` ## autoconf > 导入:`import("package.tools.autoconf")` ### autoconf.install 通过 GNU Autotools(configure/make/make install)安装包。 #### 函数原型 ::: tip API ```lua autoconf.install(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。配置参数列表 | | opt | 可选。选项参数,支持以下选项:- `packagedeps` - 包依赖列表 | #### 返回值说明 无返回值 #### 用法说明 #### 基础用法 ```lua on_install(function (package) local configs = {} table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) import("package.tools.autoconf").install(package, configs) end) ``` * 用 `--enable-static` 和 `--enable-shared` 控制静态/动态库构建,详见 [libx11 示例](https://raw.githubusercontent.com/xmake-io/xmake-repo/refs/heads/dev/packages/l/libx11/xmake.lua)。 #### 进阶用法 ```lua on_install(function (package) local configs = {} table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) if package:is_debug() then table.insert(configs, "--enable-debug") end local packagedeps = {"zlib"} import("package.tools.autoconf").install(package, configs, {packagedeps = packagedeps}) end) ``` ### autoconf.build 通过 Autotools 构建包(configure/make)。 #### 函数原型 ::: tip API ```lua autoconf.build(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.autoconf").build(package, {"--enable-static=yes"}) end) ``` ### autoconf.configure 运行 Autotools 项目的 configure 脚本。 #### 函数原型 ::: tip API ```lua autoconf.configure(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.autoconf").configure(package, {"--prefix=/usr/local"}) end) ``` ### autoconf.make 自定义参数调用 make。 #### 函数原型 ::: tip API ```lua autoconf.make(package: , argv:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | argv | 必需。make 参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.autoconf").make(package, {"install"}) end) ``` ## meson > 导入:`import("package.tools.meson")` ### meson.install 通过 Meson(setup/compile/install)安装包。 #### 函数原型 ::: tip API ```lua meson.install(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。Meson 配置参数列表 | | opt | 可选。选项参数,支持以下选项:- `packagedeps` - 包依赖列表 | #### 返回值说明 无返回值 #### 用法说明 典型用法: ```lua add_deps("meson", "ninja") on_install(function (package) local configs = {} table.insert(configs, "-Ddefault_library=" .. (package:config("shared") and "shared" or "static")) if package:is_debug() then table.insert(configs, "-Dbuildtype=debug") else table.insert(configs, "-Dbuildtype=release") end local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.meson").install(package, configs, {packagedeps = packagedeps}) end) ``` ### meson.build 通过 Meson 构建包(setup/compile)。 #### 函数原型 ::: tip API ```lua meson.build(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。Meson 配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.meson").build(package, {"-Ddefault_library=static"}) end) ``` ### meson.generate 仅生成 Meson 构建文件(setup)。 #### 函数原型 ::: tip API ```lua meson.generate(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。Meson 配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.meson").generate(package, {"-Dbuildtype=release"}) end) ``` ## make > 导入:`import("package.tools.make")` ### make.install 通过 Make 构建并安装包(build + install)。 #### 函数原型 ::: tip API ```lua make.install(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。配置参数列表 | | opt | 可选。选项参数,支持以下选项:- `packagedeps` - 包依赖列表 | #### 返回值说明 无返回值 #### 用法说明 典型用法: ```lua add_deps("make") on_install(function (package) local configs = {} local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.make").install(package, configs, {packagedeps = packagedeps}) end) ``` ### make.build 通过 Make 构建包。 #### 函数原型 ::: tip API ```lua make.build(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.make").build(package, {"CC=gcc"}) end) ``` ### make.make 自定义参数调用 make。 #### 函数原型 ::: tip API ```lua make.make(package: , argv:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | argv | 必需。make 参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.make").make(package, {"install"}) end) ``` ## ninja > 导入:`import("package.tools.ninja")` ### ninja.install 通过 Ninja 构建并安装包(build + install)。 #### 函数原型 ::: tip API ```lua ninja.install(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。配置参数列表 | | opt | 可选。选项参数,支持以下选项:- `packagedeps` - 包依赖列表 | #### 返回值说明 无返回值 #### 用法说明 典型用法: ```lua add_deps("ninja") on_install(function (package) local configs = {} local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.ninja").install(package, configs, {packagedeps = packagedeps}) end) ``` ### ninja.build 通过 Ninja 构建包。 #### 函数原型 ::: tip API ```lua ninja.build(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。配置参数列表 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 示例: ```lua on_install(function (package) import("package.tools.ninja").build(package) end) ``` ## msbuild > 导入:`import("package.tools.msbuild")` ### msbuild.build 通过 MSBuild(Visual Studio 工程)构建包。 #### 函数原型 ::: tip API ```lua msbuild.build(package: , configs:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | configs | 必需。MSBuild 配置参数列表 | | opt | 可选。选项参数,支持以下选项:- `packagedeps` - 包依赖列表 | #### 返回值说明 无返回值 #### 用法说明 典型用法: ```lua on_install(function (package) local configs = {} if package:config("configuration") then table.insert(configs, "/p:Configuration=" .. package:config("configuration")) end local packagedeps = {"zlib"} if package:config("openssl") then table.insert(packagedeps, "openssl") end import("package.tools.msbuild").build(package, configs, {packagedeps = packagedeps}) -- 需要手动拷贝生成的二进制文件 end) ``` ## xmake > 导入:`import("package.tools.xmake")` ### xmake.install 通过 xmake 自身构建和安装包,适用于: * 移植一些三方库(原生编译不过时),可通过自定义 xmake.lua 适配后再用 xmake.install 安装。 * 源码本身就维护有 xmake.lua 的项目包构建,无需额外配置,直接用 xmake.install 即可。 #### 函数原型 ::: tip API ```lua xmake.install(package: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 必需。包实例对象 | | opt | 可选。选项参数 | #### 返回值说明 无返回值 #### 用法说明 #### 针对已有 xmake.lua 的项目 如果源码目录下已有标准的 xmake.lua,直接: ```lua on_install(function (package) import("package.tools.xmake").install(package) end) ``` 无需额外配置,xmake 会自动处理动态/静态库和调试/发布模式。 #### 针对无构建系统的三方库移植 如果源码没有现成可用的构建系统,可用如下方式生成自定义 xmake.lua: ```lua on_install(function (package) io.writefile("xmake.lua", [[ add_rules("mode.debug", "mode.release") add_requires("libpng") target("bpg") set_kind("static") add_files("libbpg.c") add_files("libavcodec/hevc_cabac.c", "libavcodec/hevc_filter.c", "libavcodec/hevc.c", "libavcodec/hevcpred.c", "libavcodec/hevc_refs.c") add_files("libavcodec/hevcdsp.c", "libavcodec/hevc_mvs.c", "libavcodec/hevc_ps.c", "libavcodec/hevc_sei.c") add_files("libavcodec/utils.c", "libavcodec/cabac.c", "libavcodec/golomb.c", "libavcodec/videodsp.c") add_files("libavutil/mem.c", "libavutil/buffer.c", "libavutil/log2_tab.c", "libavutil/frame.c", "libavutil/pixdesc.c", "libavutil/md5.c") add_includedirs(".") add_headerfiles("libbpg.h") add_defines("HAVE_AV_CONFIG_H", "USE_PRED", "USE_VAR_BIT_DEPTH") on_load(function (target) local version = io.readfile("VERSION") target:add("defines", "CONFIG_BPG_VERSION=" .. version) end) ]]) import("package.tools.xmake").install(package) end) ``` * 这种方式适合源码无构建系统时移植。参考真实 [libbpg 包脚本](https://github.com/xmake-io/xmake-repo/blob/23766d5855508e69f4cb1a4375ab3b865295fcb3/packages/l/libbpg/xmake.lua)。 ## 参考 * [xmake-repo 包脚本](https://github.com/xmake-io/xmake-repo) * [官方文档:分发包](/zh/guide/package-management/package-distribution) --- --- url: /guide/basic-commands/pack-programs.md --- # Packing Programs {#pack-programs} Xmake mainly provides the following three packaging methods for distributing the target program externally: ## Generate local package {#local-package} Through the `xmake package` command, we can generate a local package with an xmake.lua configuration file, which contains all the target program binaries and can be introduced and used through the `add_requires` package management interface. The package directory structure is as follows: ```sh tree build/packages/f/foo/ build/packages/f/foo/ ├── macosx │   └── x86_64 │   └── release │   ├── include │   │   └── foo.h │   └── lib │   └── libfoo.a └── xmake.lua ``` This is generally used to distribute binary libraries and for local integration. For more detailed introduction, please refer to the document: [Using Local Packages](/guide/package-management/using-local-packages). In addition, this method can be used with [built-in macro plugins](/guide/extensions/builtin-plugins#builtin-macros) to achieve universal binary packaging for iOS. ```sh $ xmake macro package -p iphoneos -a "arm64,x86_64" $ tree ./build/foo.pkg/ ./build/foo.pkg/ ├── iphoneos │   ├── arm64 │   │   └── lib │   │   └── release │   │   └── libfoo.a │   ├── universal │   │   └── lib │   │   └── release │   │   └── libfoo.a │   └── x86_64 │   └── lib │   └── release │   └── libfoo.a └── xmake.lua ``` ## Generate remote package {#remote-package} We can also use the `xmake package -f` command to generate a remote package for submission to the repository for distribution. This package is similar to a local package and also has an xmake.lua configuration file, but the difference is that it does not directly store binary libraries, but only has a configuration file. We can submit this package configuration file to the [xmake-repo](https://github.com/xmake-io/xmake-repo) official repository for distribution, or submit it to a self-built private repository. However, the generated configuration file may not be directly usable. It only generates a rough template. The user still needs to edit and modify it to adjust the corresponding installation and test logic. For specific details, see the document: [Generate Remote Package](/guide/package-management/package-distribution#generate-package-configuration). ## Generate installation package (XPack) {#xpack} The last packaging method is the most powerful, implemented through the `xmake pack` plugin command. It is comparable to CMake's CPack packaging and can provide packaging for various system installation packages to achieve the distribution of target programs. * Windows NSIS binary installation package * Windows WIX binary installation package * runself (shell) self-compiled installation package * zip/tar.gz binary package * zip/tar.gz source package * RPM binary installation package * SRPM source installation package * DEB binary installation package It provides a complete packaging configuration mechanism, which can write flexible configuration scripts and generate more customized installation packages—not only binary packages, but also source packages, self-compiled installation packages, and archive packages. For example, generate Windows NSIS installation packages. ```sh $ xmake pack -f nsis ``` ![](/assets/img/manual/nsis_3.png) For more details, please refer to the document: [XPack Packaging](/guide/extensions/builtin-plugins#generate-installation-package-xpack). --- --- url: /api/scripts/builtin-modules/pairs.md --- # pairs * Used to traverse the dictionary #### Function Prototype ::: tip API ```lua pairs(t:
, f: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | t | Table to traverse | | f | Function to process values (optional) | | ... | Variable arguments for function (optional) | #### Usage 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 ``` --- --- url: /zh/api/scripts/builtin-modules/pairs.md --- # pairs * 用于遍历字典 #### 函数原型 ::: tip API ```lua pairs(t:
, f: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | t | 要遍历的表 | | f | 处理值的函数(可选) | | ... | 函数的可变参数(可选) | #### 用法说明 这个是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 ``` --- --- url: /api/scripts/builtin-modules/path.md --- # path The path operation module implements cross-platform path operations, which is a custom module of xmake. ## path.new * Create a new path instance #### Function Prototype ::: tip API ```lua path.new(p: , transform?: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | p | Required. Path string | | transform | Optional. Path transformation function | #### Return Value | Type | Description | |------|-------------| | path | Returns a path instance | #### Usage Create a path instance: ```lua local p = path.new("/tmp/file.txt") print(p:filename()) -- Output: file.txt ``` Using a transformation function: ```lua local p = path.new("/tmp/a", function (raw_path) return "--key=" .. raw_path end) print(p:str()) -- Output: --key=/tmp/a print(p:rawstr()) -- Output: /tmp/a ``` Or call the constructor directly: ```lua local p = path("/tmp/file.txt") -- Automatically creates an instance print(p:filename()) ``` ## path.normalize * Normalize the path #### Function Prototype ::: tip API ```lua path.normalize(p: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | p | Required. Path string | #### Return Value | Type | Description | |------|-------------| | string | Returns the normalized path string | #### Usage Normalize the path (simplify `.` and `..`): ```lua print(path.normalize("/tmp/./../file.txt")) -- Output: /file.txt print(path.normalize("c:\\tmp\\..\\..")) -- On Windows: c:\\.. ``` ## path.join * Stitching path #### Function Prototype ::: tip API ```lua path.join(paths: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | paths | Path string or array | | ... | Variable arguments, can pass multiple path strings | #### Usage Adding multiple path items by splicing. Due to the path difference of `windows/unix` style, using api to append paths is more cross-platform, for example: ```lua print(path.join("$(tmpdir)", "dir1", "dir2", "file.txt")) ``` The above splicing on Unix is equivalent to: `$(tmpdir)/dir1/dir2/file.txt`, and on Windows is equivalent to: `$(tmpdir)\\dir1\\dir2\\file.txt` If you find this cumbersome and not clear enough, you can use: [path.translate](#path-translate) to format the conversion path string to the format supported by the current platform. ## path.translate * Convert path to the path style of the current platform #### Function Prototype ::: tip API ```lua path.translate(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string to convert | #### Usage Formatting converts the specified path string to the path style supported by the current platform, and supports the path string parameter of the `windows/unix` format to be passed in, even mixed, such as: ```lua print(path.translate("$(tmpdir)/dir/file.txt")) print(path.translate("$(tmpdir)\\dir\\file.txt")) print(path.translate("$(tmpdir)\\dir/dir2//file.txt")) ``` The path strings of the above three different formats, after being standardized by `translate`, will become the format supported by the current platform, and the redundant path separator will be removed. ## path.basename * Get the file name with no suffix at the end of the path #### Function Prototype ::: tip API ```lua path.basename(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string | #### Usage ```lua print(path.basename("$(tmpdir)/dir/file.txt")) ``` The result is: `file` ## path.filename * Get the file name with the last suffix of the path #### Function Prototype ::: tip API ```lua path.filename(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string | #### Usage ```lua print(path.filename("$(tmpdir)/dir/file.txt")) ``` The result is: `file.txt` ## path.extension * Get the suffix of the path #### Function Prototype ::: tip API ```lua path.extension(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string | #### Usage ```lua print(path.extension("$(tmpdir)/dir/file.txt")) ``` The result is: `.txt` ## path.directory * Get the directory name of the path #### Function Prototype ::: tip API ```lua path.directory(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string | #### Usage ```lua print(path.directory("$(tmpdir)/dir/file.txt")) ``` The result is: `$(tmpdir)/dir` ## path.relative * Convert to relative path #### Function Prototype ::: tip API ```lua path.relative(path: , rootdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string to convert | | rootdir | Root directory for relative conversion | #### Usage ```lua print(path.relative("$(tmpdir)/dir/file.txt", "$(tmpdir)")) ``` The result is: `dir/file.txt` The second parameter is to specify the relative root directory. If not specified, the default is relative to the current directory: ```lua os.cd("$(tmpdir)") print(path.relative("$(tmpdir)/dir/file.txt")) ``` The result is the same. ## path.absolute * Convert to absolute path #### Function Prototype ::: tip API ```lua path.absolute(path: , rootdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string to convert | | rootdir | Root directory for absolute conversion | #### Usage ```lua print(path.absolute("dir/file.txt", "$(tmpdir)")) ``` The result is: `$(tmpdir)/dir/file.txt` The second parameter is to specify the relative root directory. If not specified, the default is relative to the current directory: ```lua os.cd("$(tmpdir)") print(path.absolute("dir/file.txt")) ``` The result is the same. ## path.is\_absolute * Determine if it is an absolute path #### Function Prototype ::: tip API ```lua path.is_absolute(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string to check | #### Usage ```lua if path.is_absolute("/tmp/file.txt") then -- if it is an absolute path end ``` ## path.split * Split the path by the separator #### Function Prototype ::: tip API ```lua path.split(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string to split | #### Usage ```lua print(path.split("/tmp/file.txt")) ``` The result is: `{ "tmp", "file.txt" }` ## path.sep * Return the current separator, usually `/` #### Function Prototype ::: tip API ```lua path.sep() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | None | No parameters | #### Usage ```lua print(path.sep()) ``` The result is: `/` ## path.islastsep * Get if the last character is a separator #### Function Prototype ::: tip API ```lua path.islastsep(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string to check | #### Usage ```lua if (path.islastsep("/tmp/dir/")) then -- if the last character is a separator end ``` ## path.splitenv * Split a environment variable value of an array of pathes #### Function Prototype ::: tip API ```lua path.splitenv(envpath: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | envpath | Environment variable path string | #### Usage ```lua local pathes = path.splitenv(vformat("$(env PATH)")) -- for windows local pathes = path.splitenv("C:\\Windows;C:\\Windows\\System32") -- got { "C:\\Windows", "C:\\Windows\\System32" } -- for *nix local pathes = path.splitenv("/usr/bin:/usr/local/bin") -- got { "/usr/bin", "/usr/local/bin" } ``` The result is an array of strings, each item is a path in the input string. ## path.joinenv * Concat two environment variable by the environment separator #### Function Prototype ::: tip API ```lua path.joinenv(paths: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | paths | Array of path strings | #### Usage ```lua print(path.joinenv({"/tmp/dir", "/tmp/dir2"})) ``` The result is: `/tmp/dir;/tmp/dir2` (on Windows) ## path.envsep * Get the environment separator #### Function Prototype ::: tip API ```lua path.envsep() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | None | No parameters | #### Usage ```lua print(path.envsep()) ``` The result is: `;` ## path.cygwin\_path * Get the converted MSYS2/Cygwin style path #### Function Prototype ::: tip API ```lua path.cygwin_path(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Windows path string to convert | #### Usage ```lua print(path.cygwin_path("C:\\Windows")) ``` The result is: `/C/Windows` ## path.pattern * Convert path pattern to lua pattern #### Function Prototype ::: tip API ```lua path.pattern(path: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | path | Path string to convert | #### Usage ```lua print(path.pattern("/tmp/file.txt")) ``` The result is: `/[tT][mM][pP]/[fF][iI][lL][eE]%.[tT][xX][tT]` --- --- url: /zh/api/scripts/builtin-modules/path.md --- # path 路径操作模块,实现跨平台的路径操作,这是 xmake 的一个自定义的模块。 ## path.new * 创建新的路径实例 #### 函数原型 ::: tip API ```lua path.new(p: , transform?: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | p | 必需。路径字符串 | | transform | 可选。路径转换函数 | #### 返回值说明 | 类型 | 描述 | |------|------| | path | 返回路径实例 | #### 用法说明 创建一个路径实例: ```lua local p = path.new("/tmp/file.txt") print(p:filename()) -- 输出: file.txt ``` 使用转换函数: ```lua local p = path.new("/tmp/a", function (raw_path) return "--key=" .. raw_path end) print(p:str()) -- 输出: --key=/tmp/a print(p:rawstr()) -- 输出: /tmp/a ``` 也可以直接调用构造函数: ```lua local p = path("/tmp/file.txt") -- 自动创建实例 print(p:filename()) ``` ## path.normalize * 规范化路径 #### 函数原型 ::: tip API ```lua path.normalize(p: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | p | 必需。路径字符串 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 返回规范化后的路径字符串 | #### 用法说明 规范化路径(简化 `.` 和 `..`): ```lua print(path.normalize("/tmp/./../file.txt")) -- 输出: /file.txt print(path.normalize("c:\\tmp\\..\\..")) -- 在 Windows 上输出: c:\\.. ``` ## path.join * 拼接路径 #### 函数原型 ::: tip API ```lua path.join(paths: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | paths | 路径字符串或数组 | | ... | 可变参数,可传递多个路径字符串 | #### 用法说明 将多个路径项进行追加拼接,由于`windows/unix`风格的路径差异,使用api来追加路径更加跨平台,例如: ```lua print(path.join("$(tmpdir)", "dir1", "dir2", "file.txt")) ``` 上述拼接在unix上相当于:`$(tmpdir)/dir1/dir2/file.txt`,而在windows上相当于:`$(tmpdir)\\dir1\\dir2\\file.txt` 如果觉得这样很繁琐,不够清晰简洁,可以使用:[path.translate](#path-translate)方式,格式化转换路径字符串到当前平台支持的格式。 ## path.translate * 转换路径到当前平台的路径风格 #### 函数原型 ::: tip API ```lua path.translate(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 要转换的路径字符串 | #### 用法说明 格式化转化指定路径字符串到当前平台支持的路径风格,同时支持`windows/unix`格式的路径字符串参数传入,甚至混合传入,例如: ```lua print(path.translate("$(tmpdir)/dir/file.txt")) print(path.translate("$(tmpdir)\\dir\\file.txt")) print(path.translate("$(tmpdir)\\dir/dir2//file.txt")) ``` 上面这三种不同格式的路径字符串,经过`translate`规范化后,就会变成当前平台支持的格式,并且会去掉冗余的路径分隔符。 ## path.basename * 获取路径最后不带后缀的文件名 #### 函数原型 ::: tip API ```lua path.basename(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 路径字符串 | #### 用法说明 ```lua print(path.basename("$(tmpdir)/dir/file.txt")) ``` 显示结果为:`file` ## path.filename * 获取路径最后带后缀的文件名 #### 函数原型 ::: tip API ```lua path.filename(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 路径字符串 | #### 用法说明 ```lua print(path.filename("$(tmpdir)/dir/file.txt")) ``` 显示结果为:`file.txt` ## path.extension * 获取路径的后缀名 #### 函数原型 ::: tip API ```lua path.extension(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 路径字符串 | #### 用法说明 ```lua print(path.extension("$(tmpdir)/dir/file.txt")) ``` 显示结果为:`.txt` ## path.directory * 获取路径的目录名 #### 函数原型 ::: tip API ```lua path.directory(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 路径字符串 | #### 用法说明 ```lua print(path.directory("$(tmpdir)/dir/file.txt")) ``` 显示结果为:`$(tmpdir)/dir` ## path.relative * 转换成相对路径 #### 函数原型 ::: tip API ```lua path.relative(path: , rootdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 要转换的路径字符串 | | rootdir | 相对转换的根目录 | #### 用法说明 ```lua print(path.relative("$(tmpdir)/dir/file.txt", "$(tmpdir)")) ``` 显示结果为:`dir/file.txt` 第二个参数是指定相对的根目录,如果不指定,则默认相对当前目录: ```lua os.cd("$(tmpdir)") print(path.relative("$(tmpdir)/dir/file.txt")) ``` 这样结果是一样的。 ## path.absolute * 转换成绝对路径 #### 函数原型 ::: tip API ```lua path.absolute(path: , rootdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 要转换的路径字符串 | | rootdir | 绝对转换的根目录 | #### 用法说明 ```lua print(path.absolute("dir/file.txt", "$(tmpdir)")) ``` 显示结果为:`$(tmpdir)/dir/file.txt` 第二个参数是指定相对的根目录,如果不指定,则默认相对当前目录: ```lua os.cd("$(tmpdir)") print(path.absolute("dir/file.txt")) ``` 这样结果是一样的。 ## path.is\_absolute * 判断是否为绝对路径 #### 函数原型 ::: tip API ```lua path.is_absolute(path: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | path | 要检查的路径字符串 | #### 用法说明 ```lua if path.is_absolute("/tmp/file.txt") then -- 如果是绝对路径 end ``` ## path.splitenv * 分割环境变量中的路径 #### 函数原型 ::: tip API ```lua path.splitenv(envpath: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | envpath | 环境变量路径字符串 | #### 用法说明 ```lua local pathes = path.splitenv(vformat("$(env PATH)")) -- for windows local pathes = path.splitenv("C:\\Windows;C:\\Windows\\System32") -- got { "C:\\Windows", "C:\\Windows\\System32" } -- for *nix local pathes = path.splitenv("/usr/bin:/usr/local/bin") -- got { "/usr/bin", "/usr/local/bin" } ``` 结果为一个包含了输入字符串中路径的数组。 --- --- url: /guide/best-practices/performance.md --- # Performance Optimization {#performance} ## Parallel Compilation We can speed up compilation by specifying the parallelism of tasks to be built simultaneously with `xmake -j N`. However, by default, Xmake has enabled this feature, and will automatically evaluate and allocate the number of tasks that need to be parallelized based on the CPU core resources of the current machine. ## Compilation Cache Acceleration Xmake enables local compilation cache by default, which has a very obvious speed-up effect on Linux/macOS. However, on Windows, the startup process is too heavy and the built-in preprocessor of MSVC is too slow, so the local cache is currently disabled for MSVC by default. In addition to local cache, Xmake also provides support for remote cache, which is very useful when sharing compilation cache on multiple machines. For a detailed introduction to this feature, see the document: [Compilation Cache](/guide/extras/build-cache). ## Unity Build compilation acceleration For C++ builds, we can also configure Unity Build compilation to merge multiple C++ source files into one source file for compilation to reduce the parsing time of header files, and the effect is also obvious. For details, see: [Unity Build compilation](/guide/extras/unity-build) ## Distributed compilation For super large projects, we can also speed up compilation by adding multiple compilation servers and using the distributed compilation feature. For details, see: [Distributed compilation](/guide/extras/distributed-compilation) --- --- url: /api/scripts/extension-modules/core/base/pipe.md --- # pipe The pipe module provides pipe communication functionality, supporting both anonymous pipes and named pipes, which can be used for inter-process communication. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.pipe")` ::: ## pipe.openpair * Create an anonymous pipe pair #### Function Prototype ::: tip API ```lua pipe.openpair(mode: , buffsize: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | mode | Optional. Pipe mode, default is "AA" | | buffsize | Optional. Buffer size, default is 0 (system default) | Available modes: * `"BB"`: Both read and write in blocking mode * `"BA"`: Read blocking, write non-blocking * `"AB"`: Read non-blocking, write blocking * `"AA"`: Both read and write in non-blocking mode (default) #### Usage Creates a pair of anonymous pipes, returning read and write pipe objects. Anonymous pipes are mainly used for communication between related processes (such as parent-child processes). A common scenario is redirecting subprocess input/output. Basic usage example: ```lua import("core.base.pipe") import("core.base.bytes") -- Create pipe pair local rpipe, wpipe = pipe.openpair() local buff = bytes(8192) -- Write data wpipe:write("hello xmake!", {block = true}) -- Read data local read, data = rpipe:read(buff, 13) if read > 0 and data then print(data:str()) -- Output: hello xmake! end rpipe:close() wpipe:close() ``` Used with `os.execv` to redirect subprocess output: ```lua import("core.base.pipe") import("core.base.bytes") -- Create pipe pair local rpipe, wpipe = pipe.openpair() -- Redirect subprocess stdout to pipe write end os.execv("echo", {"hello from subprocess"}, {stdout = wpipe}) -- Close write end, read subprocess output wpipe:close() local buff = bytes(8192) local read, data = rpipe:read(buff, 8192) if read > 0 then print("Subprocess output:", data:str()) end rpipe:close() ``` ## pipe.open * Open a named pipe #### Function Prototype ::: tip API ```lua pipe.open(name: , mode: , buffsize: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Pipe name | | mode | Required. Open mode | | buffsize | Optional. Buffer size, default is 0 | Available modes: * `"r"` or `"rA"`: Read-only, non-blocking (client-side) * `"w"` or `"wA"`: Write-only, non-blocking (server-side) * `"rB"`: Read-only, blocking * `"wB"`: Write-only, blocking #### Usage Opens or creates a named pipe. Named pipes can be used for communication between completely independent processes without any relationship. Similar to local sockets but more lightweight. Suitable for scenarios where data needs to be passed between different applications. ### Server-side Example ```lua import("core.base.pipe") -- Open named pipe (server-side) local pipefile = pipe.open("test", 'w') local count = 0 while count < 10000 do local write = pipefile:write("hello world..", {block = true}) if write <= 0 then break end count = count + 1 end print("Write successful, count:", count) pipefile:close() ``` ### Client-side Example ```lua import("core.base.pipe") import("core.base.bytes") -- Open named pipe (client-side) local pipefile = pipe.open("test", 'r') local buff = bytes(8192) -- Connect to server if pipefile:connect() > 0 then print("Connected") local count = 0 while count < 10000 do local read, data = pipefile:read(buff, 13, {block = true}) if read > 0 then count = count + 1 else break end end print("Read successful, count:", count) end pipefile:close() ``` ## pipe:read * Read data from pipe #### Function Prototype ::: tip API ```lua pipe:read(buff: , size: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | buff | Required. bytes buffer object to store the read data | | size | Required. Number of bytes to read | | opt | Optional. Option parameters | Options: * `block`: Whether to block reading, default false * `start`: Buffer start position, default 1 * `timeout`: Timeout in milliseconds, default -1 (infinite wait) #### Return Values | Type | Description | |------|-------------| | read | Actual number of bytes read | | data | Read data (bytes object) | #### Usage Reads data from the pipe into the specified buffer. Non-blocking mode (default) returns immediately, may return 0 indicating no data available. Blocking mode waits until the specified amount of data is read or an error occurs: ```lua import("core.base.bytes") local buff = bytes(8192) -- Blocking read 100 bytes, timeout 5 seconds local read, data = rpipe:read(buff, 100, {block = true, timeout = 5000}) if read > 0 then print("Read:", data:str()) end ``` ## pipe:write * Write data to pipe #### Function Prototype ::: tip API ```lua pipe:write(data: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Data to write, can be string or bytes object | | opt | Optional. Option parameters | Options: * `block`: Whether to block writing, default false * `start`: Data start position, default 1 * `last`: Data end position, default is data size * `timeout`: Timeout in milliseconds, default -1 #### Return Value | Type | Description | |------|-------------| | write | Actual number of bytes written | #### Usage Writes data to the pipe. Non-blocking mode (default) may only write partial data. Blocking mode waits until all data is successfully written: ```lua -- Blocking write data local write = wpipe:write("hello world", {block = true}) if write > 0 then print("Wrote", write, "bytes") end ``` ## pipe:connect * Connect to named pipe (server-side) #### Function Prototype ::: tip API ```lua pipe:connect(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters | Options: * `timeout`: Timeout in milliseconds, default -1 #### Return Value | Type | Description | |------|-------------| | number | Returns a positive number | #### Usage Connects to a named pipe, only used on the server-side of named pipes. After creating a named pipe on the server, call this method to wait for client connection. ```lua import("core.base.pipe") local pipefile = pipe.open("test", 'r') if pipefile:connect() > 0 then print("Client connected") -- Can start reading/writing data end ``` ## pipe:wait * Wait for pipe events #### Function Prototype ::: tip API ```lua pipe:wait(events: , timeout: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | events | Required. Events to wait for | | timeout | Required. Timeout in milliseconds, -1 means wait indefinitely | Event constants: * `pipe.EV_READ` (1): Readable event, indicates pipe has data to read * `pipe.EV_WRITE` (2): Writable event, indicates pipe can accept data * `pipe.EV_CONN` (2): Connection event, used for named pipes to wait for client connection #### Return Value | Type | Description | |------|-------------| | number | Returns the actual event constant value that occurred | #### Usage Waits for specified pipe events to occur. In non-blocking mode, this method can be used to implement event-driven I/O. In non-blocking mode, wait can be used to implement efficient event loops: ```lua -- Wait for pipe to be readable, timeout 1 second local events = rpipe:wait(pipe.EV_READ, 1000) if events == pipe.EV_READ then -- Pipe is readable, can read data local read, data = rpipe:read(buff, 100) end -- Wait for pipe to be writable local events = wpipe:wait(pipe.EV_WRITE, 1000) if events == pipe.EV_WRITE then -- Pipe is writable, can write data wpipe:write("data") end ``` ## pipe:close * Close the pipe #### Function Prototype ::: tip API ```lua pipe:close() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Closes the pipe and releases resources. Pipes should be closed promptly after use. ## pipe:name * Get pipe name #### Function Prototype ::: tip API ```lua pipe:name() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Gets the name of a named pipe. Returns nil for anonymous pipes. ::: tip TIP Pipes are unidirectional, one end can only read, the other can only write. For bidirectional communication, two pipes are needed. ::: ::: warning WARNING Remember to call `close()` after using the pipe to release resources. A bytes buffer must be created using `bytes()` before reading data. ::: --- --- url: /zh/api/scripts/extension-modules/core/base/pipe.md --- # pipe pipe 模块提供了管道通信功能,支持匿名管道和命名管道,可用于进程间通信。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.pipe")` ::: ## pipe.openpair * 创建匿名管道对 #### 函数原型 ::: tip API ```lua pipe.openpair(mode: , buffsize: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | mode | 可选。管道模式,默认为 "AA" | | buffsize | 可选。缓冲区大小,默认为 0(系统默认) | #### 用法说明 创建一对匿名管道,返回读端和写端两个管道对象。 匿名管道主要用于有亲缘关系的进程间通信(如父子进程),常见场景是重定向子进程的输入输出。 管道模式说明: * `"BB"`:读写都是阻塞模式 * `"BA"`:读阻塞,写非阻塞 * `"AB"`:读非阻塞,写阻塞 * `"AA"`:读写都是非阻塞模式(默认) 基本使用示例: ```lua import("core.base.pipe") import("core.base.bytes") -- 创建管道对 local rpipe, wpipe = pipe.openpair() local buff = bytes(8192) -- 写入数据 wpipe:write("hello xmake!", {block = true}) -- 读取数据 local read, data = rpipe:read(buff, 13) if read > 0 and data then print(data:str()) -- 输出: hello xmake! end rpipe:close() wpipe:close() ``` 配合 `os.execv` 重定向子进程输出: ```lua import("core.base.pipe") import("core.base.bytes") -- 创建管道对 local rpipe, wpipe = pipe.openpair() -- 将子进程的 stdout 重定向到管道写端 os.execv("echo", {"hello from subprocess"}, {stdout = wpipe}) -- 关闭写端,读取子进程输出 wpipe:close() local buff = bytes(8192) local read, data = rpipe:read(buff, 8192) if read > 0 then print("子进程输出:", data:str()) end rpipe:close() ``` ## pipe.open * 打开命名管道 #### 函数原型 ::: tip API ```lua pipe.open(name: , mode: , buffsize: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 管道名称 | | mode | 打开模式 | | buffsize | 可选。缓冲区大小,默认为 0 | #### 用法说明 打开或创建命名管道。 命名管道可以在完全独立的进程间通信,无需亲缘关系。类似于本地 socket,但更轻量。适合需要在不同应用程序间传递数据的场景。 打开模式说明: * `"r"` 或 `"rA"`:只读,非阻塞(客户端) * `"w"` 或 `"wA"`:只写,非阻塞(服务端) * `"rB"`:只读,阻塞 * `"wB"`:只写,阻塞 ### 服务端示例 ```lua import("core.base.pipe") -- 打开命名管道(服务端) local pipefile = pipe.open("test", 'w') local count = 0 while count < 10000 do local write = pipefile:write("hello world..", {block = true}) if write <= 0 then break end count = count + 1 end print("写入成功, count:", count) pipefile:close() ``` ### 客户端示例 ```lua import("core.base.pipe") import("core.base.bytes") -- 打开命名管道(客户端) local pipefile = pipe.open("test", 'r') local buff = bytes(8192) -- 连接到服务端 if pipefile:connect() > 0 then print("已连接") local count = 0 while count < 10000 do local read, data = pipefile:read(buff, 13, {block = true}) if read > 0 then count = count + 1 else break end end print("读取成功, count:", count) end pipefile:close() ``` ## pipe:read * 从管道读取数据 #### 函数原型 ::: tip API ```lua pipe:read(buff: , size: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | buff | bytes 缓冲区对象,用于存储读取的数据 | | size | 要读取的字节数 | | opt | 可选。选项参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | read | 实际读取的字节数 | | data | 读取的数据(bytes 对象) | #### 用法说明 从管道读取数据到指定的缓冲区。 选项参数说明: * `block`:是否阻塞读取,默认 false * `start`:缓冲区起始位置,默认 1 * `timeout`:超时时间(毫秒),默认 -1(无限等待) 非阻塞模式(默认)会立即返回,可能返回 0 表示暂无数据。阻塞模式会等待直到读取到指定大小的数据或发生错误: ```lua import("core.base.bytes") local buff = bytes(8192) -- 阻塞读取 100 字节,超时 5 秒 local read, data = rpipe:read(buff, 100, {block = true, timeout = 5000}) if read > 0 then print("读取:", data:str()) end ``` ## pipe:write * 向管道写入数据 #### 函数原型 ::: tip API ```lua pipe:write(data: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 要写入的数据,可以是字符串或 bytes 对象 | | opt | 可选。选项参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | write | 实际写入的字节数 | #### 用法说明 向管道写入数据。 选项参数说明: * `block`:是否阻塞写入,默认 false * `start`:数据起始位置,默认 1 * `last`:数据结束位置,默认为数据大小 * `timeout`:超时时间(毫秒),默认 -1 非阻塞模式(默认)可能只写入部分数据。阻塞模式会等待直到所有数据都写入成功: ```lua -- 阻塞写入数据 local write = wpipe:write("hello world", {block = true}) if write > 0 then print("写入了", write, "字节") end ``` ## pipe:connect * 连接命名管道(服务端) #### 函数原型 ::: tip API ```lua pipe:connect(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 成功返回正数 | #### 用法说明 连接命名管道,仅用于命名管道的服务端。在服务端创建命名管道后,需要调用此方法等待客户端连接。 选项参数说明: * `timeout`:超时时间(毫秒),默认 -1 ```lua import("core.base.pipe") local pipefile = pipe.open("test", 'r') if pipefile:connect() > 0 then print("客户端已连接") -- 可以开始读写数据 end ``` ## pipe:wait * 等待管道事件 #### 函数原型 ::: tip API ```lua pipe:wait(events: , timeout: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | events | 要等待的事件,支持事件常量 | | timeout | 超时时间(毫秒),-1 表示无限等待 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回实际发生的事件常量值 | #### 用法说明 等待指定的管道事件发生。在非阻塞模式下,可以使用此方法实现事件驱动的 I/O。 参数: * `events`:要等待的事件,支持以下事件常量: * `pipe.EV_READ` (1):可读事件,表示管道有数据可读 * `pipe.EV_WRITE` (2):可写事件,表示管道可以写入数据 * `pipe.EV_CONN` (2):连接事件,用于命名管道等待客户端连接 * `timeout`:超时时间(毫秒),-1 表示无限等待 在非阻塞模式下,可以使用 wait 实现高效的事件循环: ```lua -- 等待管道可读,超时 1 秒 local events = rpipe:wait(pipe.EV_READ, 1000) if events == pipe.EV_READ then -- 管道可读,可以读取数据 local read, data = rpipe:read(buff, 100) end -- 等待管道可写 local events = wpipe:wait(pipe.EV_WRITE, 1000) if events == pipe.EV_WRITE then -- 管道可写,可以写入数据 wpipe:write("data") end ``` ## pipe:close * 关闭管道 #### 函数原型 ::: tip API ```lua pipe:close() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 关闭管道并释放资源。使用完管道后应该及时关闭。 ## pipe:name * 获取管道名称 #### 函数原型 ::: tip API ```lua pipe:name() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 获取命名管道的名称。对于匿名管道,返回 nil。 ::: tip 提示 管道是单向的,一端只能读,另一端只能写。需要双向通信时,需要创建两个管道。 ::: ::: warning 注意 使用完管道后记得调用 `close()` 释放资源。读取数据时需要预先使用 `bytes()` 创建缓冲区。 ::: --- --- url: /api/description/plugin-and-task.md --- # 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`. ## task * Defining plugins or tasks #### Function Prototype ::: tip API ```lua task(name: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Task name string | #### Usage The `task` field is used to describe a custom task implementation, in the same level as [target](/api/description/project-target) and [option](/api/description/configuration-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](#set_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 #### Function Prototype ::: tip API ```lua task_end() ``` ::: #### Parameter Description No parameters required for this function. #### Usage This is an optional api that shows the departure option scope, similar to [target\_end](/api/description/project-target#target-end). ## set\_menu * Setting the task menu #### Function Prototype ::: tip API ```lua set_menu(options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | options | Menu options table | #### Usage 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](#set_category), then this task is a plugin: ``` Plugins: ... Echo Echo the given info! ``` To run this task manually, you can execute: ```sh $ 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: ```sh 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 Environment 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. ``` ::: tip NOTE 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: ```sh $ xmake echo -c red hello xmake! ``` You can also use the full name of the option and highlight it: ```sh $ 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`. ## set\_category * Setting task categories #### Function Prototype ::: tip API ```lua set_category(category: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | category | Category name string | #### Usage 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. ::: tip NOTE 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. ## on\_run * Setting up a task to run a script #### Function Prototype ::: tip API ```lua on_run(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Task execution function | #### Usage 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](/api/scripts/builtin-modules/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](/api/scripts/builtin-modules/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 ``` ::: tip NOTE 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: [Builtin Module](/api/scripts/builtin-modules/import) and [Extension Module](/api/scripts/extension-modules/core/base/option) 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) --- --- url: /guide/extensions/plugin-development.md --- # Plugin Development ## Introduction XMake supports the plugin module and we can conveniently develop our own plugin modules. We can run the command `xmake -h` to look over some built-in 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 repeatedly. * doxygen: Generate doxygen documentation automatically. * hello: The demo plugin and only prints: 'hello xmake!' * project: Generate project files for IDEs. It can generate make, cmake, vs, xcode (needs cmake), ninja project files, compile\_commands.json, and compile\_flags.txt ## Quick Start Now let's 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: ``` plugins |-- hello | |-- xmake.lua |... | notice no xmake.lua in plugins directory ``` Now one of the most simple plugins is finished. How does xmake detect it? There are three ways: 1. Put this plugin directory into xmake/plugins in the source code as a built-in plugin. 2. Put this plugin directory into ~/.xmake/plugins as a global user plugin. 3. Put this plugin directory (hello) into the `./plugins` directory of the current project and call `add_plugindirs("plugins")` in xmake.lua as a local project plugin. ## Run Plugin Next we run this plugin ```sh xmake hello ``` The result 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) ``` --- --- url: /posts/api-import.md --- `import` is mainly used to import xmake's extension libraries and some custom library modules. It is generally used in custom scripts (`on_build`, `on_run` ..), plugin development, template development, platform extensions, custom tasks, etc. The import mechanism is as follows: 1. First import from the current script directory 2. Then import from extension libraries Import syntax rules: Based on `.` library path rules, for example: Import core extension modules: ```lua import("core.base.option") import("core.project") import("core.project.task") import("core") function main() -- Get argument options print(option.get("version")) -- Run tasks and plugins task.run("hello") project.task.run("hello") core.project.task.run("hello") end ``` Import custom modules from current directory: Directory structure: ```lua 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 public interfaces. Private interfaces are prefixed with `_` to indicate they will not be exported and will not be called externally. In addition to the current directory, we can also import libraries from other specified directories, for example: ```lua import("hello3", {rootdir = "/home/xxx/modules"}) ``` To prevent naming conflicts, you can also specify an alias after importing: ```lua import("core.platform.platform", {alias = "p"}) function main() -- This way we can use p to call the plats interface of the platform module to get all platforms supported by xmake table.dump(p.plats()) end ``` `import` can not only import libraries, but also support inheritance import at the same time, implementing inheritance relationships between modules: ```lua import("xxx.xxx", {inherit = true}) ``` This way, what is imported is not a reference to this module, but all public interfaces of the imported module itself, which will be merged with the current module's interfaces to implement inheritance between modules. --- --- url: /guide/project-configuration/plugin-and-task.md --- # Plugins and Tasks {#plugin-and-task} Xmake supports custom tasks and plugins, both implemented using the `task` system. Tasks can be used for automating build processes, code generation, file processing, and various other project requirements. ## Basic Concepts {#basic-concepts} * **Task**: Custom build steps or tools that can be called within projects * **Plugin**: Special tasks that typically provide more complex functionality, categorized with `set_category("plugin")` * **Menu**: Set via `set_menu()` to allow tasks to be called directly from the command line ## Creating Simple Tasks {#create-simple-task} ### Basic Syntax ```lua task("taskname") on_run(function () -- task execution logic print("Task executing...") end) ``` ### Example: Hello Task ```lua task("hello") on_run(function () print("hello xmake!") end) ``` This task can only be called via `task.run()` in `xmake.lua`: ```lua target("test") after_build(function (target) import("core.project.task") task.run("hello") end) ``` ## Creating Command Line Tasks {#create-cli-task} ### Setting Menu Use `set_menu()` to allow tasks to be called directly from the command line: ```lua task("echo") on_run(function () import("core.base.option") -- get parameter content and display local contents = option.get("contents") or {} local color = option.get("color") or "black" cprint("${%s}%s", color, table.concat(contents, " ")) end) set_menu { usage = "xmake echo [options]", description = "Display specified information", options = { {'c', "color", "kv", "black", "Set output color"}, {nil, "contents", "vs", nil, "Content to display"} } } ``` Now you can call it from the command line: ```sh $ xmake echo -c red hello xmake! ``` ## Task Categories {#task-categories} ### Setting Task Categories ```lua task("myplugin") set_category("plugin") -- categorize as plugin on_run(function () print("This is a plugin") end) ``` Category descriptions: * **plugin**: Displayed in "Plugins" group * **action**: Default category for built-in tasks * **custom**: Can set any custom category name ## Task Parameter Handling {#task-parameters} ### Parameter Types ```lua task("example") on_run(function () import("core.base.option") -- get different types of parameters local verbose = option.get("verbose") -- boolean local color = option.get("color") -- key-value local files = option.get("files") -- multiple values local args = {...} -- variable arguments end) set_menu { options = { {'v', "verbose", "k", nil, "Enable verbose output"}, -- boolean option {'c', "color", "kv", "red", "Set color"}, -- key-value option {'f', "files", "vs", nil, "File list"}, -- multiple values option {nil, "args", "vs", nil, "Other arguments"} -- variable arguments } } ``` ### Parameter Type Descriptions * **k**: key-only, boolean parameter * **kv**: key-value, key-value pair parameter * **v**: value, single value parameter * **vs**: values, multiple values parameter ## Using Tasks in Projects {#using-tasks-in-project} ### Execute Tasks After Build ```lua target("test") set_kind("binary") add_files("src/*.cpp") after_build(function (target) import("core.project.task") -- run code generation task after build task.run("generate-code") -- run test task task.run("run-tests") end) ``` ### Custom Build Tasks ```lua -- code generation task task("generate-code") on_run(function () print("Generating code...") -- execute code generation logic os.exec("protoc --cpp_out=src proto/*.proto") end) -- test task task("run-tests") on_run(function () print("Running tests...") os.exec("xmake run test") end) ``` ### File Processing Tasks ```lua task("process-assets") on_run(function () import("core.base.option") local input_dir = option.get("input") or "assets" local output_dir = option.get("output") or "build/assets" -- process resource files os.mkdir(output_dir) os.cp(path.join(input_dir, "*.png"), output_dir) os.cp(path.join(input_dir, "*.json"), output_dir) print("Resource file processing completed") end) set_menu { usage = "xmake process-assets [options]", description = "Process project resource files", options = { {'i', "input", "kv", "assets", "Input directory"}, {'o', "output", "kv", "build/assets", "Output directory"} } } ``` ## Complex Task Examples {#complex-task-examples} ### Example 1: Code Formatting Task ```lua task("format") on_run(function () import("core.base.option") import("lib.detect.find_tool") local tool = find_tool("clang-format") if not tool then raise("clang-format not found!") end local files = option.get("files") or {"src/**/*.cpp", "src/**/*.h"} for _, pattern in ipairs(files) do local filelist = os.files(pattern) for _, file in ipairs(filelist) do os.execv(tool.program, {"-i", file}) print("Formatting file:", file) end end end) set_menu { usage = "xmake format [options]", description = "Format code files", options = { {'f', "files", "vs", nil, "File patterns to format"} } } ``` ### Example 2: Project Cleanup Task ```lua task("clean-all") on_run(function () local patterns = { "build/**", "*.log", "*.tmp", "*.o", "*.a", "*.so", "*.dylib", "*.exe" } for _, pattern in ipairs(patterns) do os.tryrm(pattern) end print("Project cleanup completed") end) set_menu { usage = "xmake clean-all", description = "Clean all build files and temporary files" } ``` ## Task Invocation Methods {#task-invocation} ### 1. Command Line Invocation ```sh $ xmake taskname [options] [args...] ``` ### 2. Script Invocation ```lua import("core.project.task") -- call task task.run("taskname") -- pass parameters task.run("taskname", {option1 = "value1"}, "arg1", "arg2") ``` ### 3. Invocation in Build Process ```lua target("test") before_build(function (target) task.run("prepare") end) after_build(function (target) task.run("post-process") end) ``` ## Best Practices {#best-practices} 1. **Error Handling**: Use `pcall` to wrap task logic 2. **Progress Display**: Use `progress.show()` to display execution progress 3. **Parameter Validation**: Check if required parameters exist 4. **Modularization**: Use separate module files for complex tasks 5. **Documentation**: Add clear descriptions and usage instructions for tasks ## More Information {#more-information} * Complete API documentation: [Plugin and Task API](/api/description/plugin-and-task) * Built-in task reference: [Built-in Plugins](/guide/extensions/builtin-plugins) * Plugin development guide: [Plugin Development](/guide/extensions/plugin-development) --- --- url: /posts/precompiled-header.md --- Recently, in order to implement precompiled header file support for [xmake](https://xmake.io), I studied the mechanisms and differences of how major mainstream compilers handle precompiled headers. Most c/c++ compilers now support precompiled headers, such as: gcc, clang, msvc, etc., to optimize c++ code compilation speed. After all, if c++ header files contain template definitions, compilation speed is very slow. If most common header files can be placed in a `header.h` and precompiled before other source code compilation, subsequent code can reuse this part of the precompiled header, which can greatly reduce frequent redundant header file compilation. However, different compilers have different levels of support and handling methods for it, and it's not very universal. It took a lot of effort to encapsulate it into a unified interface and usage method in xmake. #### MSVC Precompiled Header Handling Precompiled headers are very common in msvc projects. You often see files like `stdafx.cpp`, `stdafx.h`, which are used for this purpose. The msvc compiler generates the precompiled header file `stdafx.pch` by compiling `stdafx.cpp`. The command line to create a precompiled header is as follows: ```bash $ cl.exe -c -Yc -Fpstdafx.pch -Fostdafx.obj stdafx.cpp ``` Among them, `-Yc` means creating the precompiled header `stdafx.pch`, `-Fp` is used to specify the output file path of `*.pch`, and `-Fo` is used to specify the object file generated by compiling `stdafx.cpp`. How do other source files use this `stdafx.pch`? By passing `stdafx.h` to `-Yu` to tell the compiler to compile the current code, ignore `#include "stdafx.h"`, and directly use the already compiled `stdafx.pch` file. ```bash $ cl.exe -c -Yustdafx.h -Fpstdafx.pch -Fotest.obj test.cpp ``` Finally, when linking, you need to link both: `stdafx.obj` and `test.obj`. This is also different from gcc and clang compilers. ```bash $ link.exe -out:test test.obj stdafx.obj ``` Note: You must also link `stdafx.obj`. Although `stdafx.cpp` is only used to generate `stdafx.pch`, the object file is also needed. Another difference from gcc and clang is that msvc's `-Yu` specifies that `stdafx.h` must be the header file name in `#include "stdafx.h"`, not the file path. #### Clang Precompiled Header File Handling I personally feel that clang's precompiled header file support is the most friendly and simplest. Compared to msvc, it doesn't need `stdafx.cpp`, only a header file `stdafx.h` can generate a pch file. Compared to gcc, it can flexibly control the pch file path, which is more flexible. Compile header file to generate pch file: ```bash $ clang -c -o stdafx.pch stdafx.h ``` Use precompiled header file: ```bash $ clang -c -include stdafx.h -include-pch stdafx.pch -o test.o test.cpp ``` Among them, `-include stdafx.h` is used to ignore `#include "stdafx.h"` when compiling `test.cpp`, and use the precompiled `stdafx.pch` through `-include-pch`. And the `stdafx.h` and `stdafx.pch` specified here can not only be files in the includedir search path, but also specify full path file names, which is very flexible, for example: ```bash $ clang -c -include inc/stdafx.h -include-pch out/stdafx.pch -o test.o test.cpp ``` #### GCC Precompiled Header File Handling gcc's precompiled header handling is basically similar to clang's. The only difference is: it doesn't support the `-include-pch` parameter, so it cannot specify the `stdafx.pch` file path to use. It has its own search rules: 1. Look for `stdafx.h.pch` file in the directory where `stdafx.h` is located 2. Look for `stdafx.h.pch` in the `-I` header file search path Compile header file to generate pch file: ```bash $ gcc -c -o stdafx.pch stdafx.h ``` Use precompiled header file: ```bash $ gcc -c -include stdafx.h -o test.o test.cpp ``` In order for the above code to compile normally, `stdafx.h.pch` must be placed in the same directory as `stdafx.h`, so that compilation can find it. I haven't found a method to specify the output directory yet. #### Other Notes For gcc and clang, compiling `*.h` header files by default is used as c precompiled headers, which is different from c++ pch and cannot be used by c++ code. If you want to generate c++ usable pch files, you must tell the compiler how to compile `stdafx.h`. This can be solved through the `-x c++-header` parameter: ```bash $ gcc -c -x c++-header -o stdafx.pch stdafx.h ``` Of course, it can also be solved by modifying the suffix: ```bash $ gcc -c -o stdafx.pch stdafx.hpp ``` #### xmake Support for Precompiled Header Files xmake supports accelerating `c/c++` program compilation through precompiled header files. Currently supported compilers are: gcc, clang and msvc. The usage for c precompiled header files is as follows: ```lua target("test") set_pcheader("header.h") ``` If it's precompilation of c++ header files, change to: ```lua target("test") set_pcxxheader("header.h") ``` For more usage instructions, see: [target.set\_pcheader](https://xmake.io/) #### References [Speed up C++ compilation, part 1: precompiled headers](https://xmake.io/) --- --- url: /api/scripts/builtin-modules/print.md --- # print * Wrapping print terminal log #### Function Prototype ::: tip API ```lua print(...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ... | Variable arguments, can pass multiple values | #### Usage 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. --- --- url: /zh/api/scripts/builtin-modules/print.md --- # print * 换行打印终端日志 #### 函数原型 ::: tip API ```lua print(...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ... | 可变参数,可传递多个值 | #### 用法说明 此接口也是lua的原生接口,xmake在原有行为不变的基础上也进行了扩展,同时支持:格式化输出、多变量输出。 先看下原生支持的方式: ```lua print("hello xmake!") print("hello", "xmake!", 123) ``` 并且同时还支持扩展的格式化写法: ```lua print("hello %s!", "xmake") print("hello xmake! %d", 123) ``` xmake会同时支持这两种写法,内部会去自动智能检测,选择输出行为。 --- --- url: /api/scripts/builtin-modules/printf.md --- # printf * No line printing terminal log #### Function Prototype ::: tip API ```lua printf(...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ... | Variable arguments, can pass multiple values | #### Usage Like the [print](/api/scripts/builtin-modules/print) interface, the only difference is that it doesn't wrap. --- --- url: /zh/api/scripts/builtin-modules/printf.md --- # printf * 无换行打印终端日志 #### 函数原型 ::: tip API ```lua printf(...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ... | 可变参数,可传递多个值 | #### 用法说明 类似[print](/zh/api/scripts/builtin-modules/print)接口,唯一的区别就是不换行。 --- --- url: /api/scripts/extension-modules/privilege/sudo.md --- # 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. ::: tip NOTE In order to ensure security, unless you must use it, try not to use this interface in other cases. ::: ## sudo.has * Determine if sudo supports #### Function Prototype ::: tip API ```lua sudo.has() ``` ::: #### Parameter Description No parameters #### Return Value | Type | Description | |------|-------------| | boolean | Returns true if sudo is supported, false otherwise | #### Usage 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 #### Function Prototype ::: tip API ```lua sudo.run(cmd: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cmd | Required. Shell command to execute | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage For specific usage, please refer to: [os.run](/api/scripts/builtin-modules/os#os-run). ```lua import("privilege.sudo") sudo.run("rm /system/file") ``` ## sudo.runv * Quietly running native shell commands with parameter list #### Function Prototype ::: tip API ```lua sudo.runv(argv:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | argv | Required. Command argument list | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage For specific usage, please refer to: [os.runv](/api/scripts/builtin-modules/os#os-runv). ## sudo.exec * Echo running native shell commands #### Function Prototype ::: tip API ```lua sudo.exec(cmd: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cmd | Required. Shell command to execute | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage For specific usage, please refer to: [os.exec](/api/scripts/builtin-modules/os#os-exec). ## sudo.execv * Echo running native shell commands with parameter list #### Function Prototype ::: tip API ```lua sudo.execv(argv:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | argv | Required. Command argument list | #### Return Value | Type | Description | |------|-------------| | boolean | Returns true on success, false on failure | #### Usage For specific usage, please refer to: [os.execv](/api/scripts/builtin-modules/os#os-execv). ## sudo.iorun * Quietly running native shell commands and getting output #### Function Prototype ::: tip API ```lua sudo.iorun(cmd: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cmd | Required. Shell command to execute | #### Return Value | Type | Description | |------|-------------| | string | Returns output content on success, nil on failure | #### Usage For specific usage, please refer to: [os.iorun](/api/scripts/builtin-modules/os#os-iorun). ## sudo.iorunv * Run the native shell command quietly and get the output with a list of parameters #### Function Prototype ::: tip API ```lua sudo.iorunv(argv:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | argv | Required. Command argument list | #### Return Value | Type | Description | |------|-------------| | string | Returns output content on success, nil on failure | #### Usage For specific usage, please refer to: [os.iorunv](/api/scripts/builtin-modules/os#os-iorunv). --- --- url: /zh/api/scripts/extension-modules/privilege/sudo.md --- # privilege.sudo 此接口用于通过`sudo`来运行命令,并且提供了平台一致性处理,对于一些需要root权限运行的脚本,可以使用此接口。 ::: tip 注意 为了保证安全性,除非必须使用的场合,其他情况下尽量不要使用此接口。 ::: ## sudo.has * 判断sudo是否支持 #### 函数原型 ::: tip API ```lua sudo.has() ``` ::: #### 参数说明 无参数 #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 支持 sudo 返回 true,不支持返回 false | #### 用法说明 目前仅在`macosx/linux`下支持sudo,windows上的管理员权限运行暂时还不支持,因此建议使用前可以通过此接口判断支持情况后,针对性处理。 ```lua import("privilege.sudo") if sudo.has() then sudo.run("rm /system/file") end ``` ## sudo.run * 安静运行原生shell命令 #### 函数原型 ::: tip API ```lua sudo.run(cmd: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cmd | 必需。要执行的 shell 命令 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 命令执行成功返回 true,失败返回 false | #### 用法说明 具体用法可参考:[os.run](/zh/api/scripts/builtin-modules/os#os-run)。 ```lua import("privilege.sudo") sudo.run("rm /system/file") ``` ## sudo.runv * 安静运行原生shell命令,带参数列表 #### 函数原型 ::: tip API ```lua sudo.runv(argv:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | argv | 必需。命令参数列表 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 命令执行成功返回 true,失败返回 false | #### 用法说明 具体用法可参考:[os.runv](/zh/api/scripts/builtin-modules/os#os-runv)。 ## sudo.exec * 回显运行原生shell命令 #### 函数原型 ::: tip API ```lua sudo.exec(cmd: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cmd | 必需。要执行的 shell 命令 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 命令执行成功返回 true,失败返回 false | #### 用法说明 具体用法可参考:[os.exec](/zh/api/scripts/builtin-modules/os#os-exec)。 ## sudo.execv * 回显运行原生shell命令,带参数列表 #### 函数原型 ::: tip API ```lua sudo.execv(argv:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | argv | 必需。命令参数列表 | #### 返回值说明 | 类型 | 描述 | |------|------| | boolean | 命令执行成功返回 true,失败返回 false | #### 用法说明 具体用法可参考:[os.execv](/zh/api/scripts/builtin-modules/os#os-execv)。 ## sudo.iorun * 安静运行原生shell命令并获取输出内容 #### 函数原型 ::: tip API ```lua sudo.iorun(cmd: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cmd | 必需。要执行的 shell 命令 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 命令执行成功返回输出内容,失败返回 nil | #### 用法说明 具体用法可参考:[os.iorun](/zh/api/scripts/builtin-modules/os#os-iorun)。 ## sudo.iorunv * 安静运行原生shell命令并获取输出内容,带参数列表 #### 函数原型 ::: tip API ```lua sudo.iorunv(argv:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | argv | 必需。命令参数列表 | #### 返回值说明 | 类型 | 描述 | |------|------| | string | 命令执行成功返回输出内容,失败返回 nil | #### 用法说明 具体用法可参考:[os.iorunv](/zh/api/scripts/builtin-modules/os#os-iorunv)。 --- --- url: /api/scripts/extension-modules/core/base/process.md --- # process The process module provides subprocess management functionality for creating, controlling, and communicating with external processes. This is the underlying module for `os.exec` and `os.execv` functions. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.process")` ::: ## process.open * Open a subprocess with command string #### Function Prototype ::: tip API ```lua process.open(command: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | command | Required. The command string to execute | | opt | Optional. Process options table | Options in `opt`: * `stdin` - Input source (file path, file object, or pipe object) * `stdout` - Output destination (file path, file object, or pipe object) * `stderr` - Error output destination (file path, file object, or pipe object) * `envs` - Environment variables array (e.g., `{"PATH=xxx", "XXX=yyy"}`) #### Usage Creates a new subprocess by executing a command string. Returns a subprocess object that can be used to control and communicate with the process. ```lua -- Basic process execution local proc = process.open("echo hello world") local ok, status = proc:wait() proc:close() -- Process with file redirection local stdout = os.tmpfile() local stderr = os.tmpfile() local proc = process.open("xmake lua print 'hello'", { stdout = stdout, stderr = stderr }) proc:wait() proc:close() -- Read output from file local output = io.readfile(stdout):trim() print(output) -- Output: hello ``` Process with environment variables: ```lua local proc = process.open("echo $MY_VAR", { envs = {"MY_VAR=hello from xmake"} }) proc:wait() proc:close() ``` ## process.openv * Open a subprocess with program and arguments list #### Function Prototype ::: tip API ```lua process.openv(program: , argv:
, opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | program | Required. The program to execute | | argv | Required. Array of arguments to pass to the program | | opt | Optional. Process options table (same as `process.open`) | #### Usage Creates a new subprocess by executing a program with a list of arguments. This is safer than `process.open` as it avoids shell interpretation issues. ```lua -- Execute program with arguments local proc = process.openv("xmake", {"lua", "print", "hello world"}) local ok, status = proc:wait() proc:close() -- Execute with file redirection local stdout = os.tmpfile() local proc = process.openv("xmake", {"lua", "print", "xmake"}, { stdout = stdout, stderr = stderr }) proc:wait() proc:close() -- Read the output local output = io.readfile(stdout):trim() print(output) -- Output: xmake ``` Execute with environment variables: ```lua local proc = process.openv("env", {"MY_VAR=test"}, { envs = {"MY_VAR=hello from xmake"} }) proc:wait() proc:close() ``` ## process:wait * Wait for subprocess to complete #### Function Prototype ::: tip API ```lua process:wait(timeout: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | timeout | Optional. Timeout in milliseconds. Use -1 for infinite wait, 0 for non-blocking | #### Usage Waits for the subprocess to complete and returns the exit status. Can be used with or without timeout. Returns: * `ok` - Exit code (0 for success, negative for error) * `status` - Process status or error message ```lua local proc = process.open("echo hello") local ok, status = proc:wait() print("Exit code:", ok) -- Output: 0 (success) print("Status:", status) -- Output: nil or error message proc:close() ``` Wait with timeout: ```lua local proc = process.open("sleep 10") local ok, status = proc:wait(1000) -- Wait max 1 second if ok < 0 then print("Process timed out or failed:", status) end proc:close() ``` Non-blocking wait: ```lua local proc = process.open("echo hello") local ok, status = proc:wait(0) -- Non-blocking if ok < 0 then print("Process not ready yet") else print("Process completed with code:", ok) end proc:close() ``` ## process:kill * Kill the subprocess #### Function Prototype ::: tip API ```lua process:kill() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Terminates the subprocess immediately. Returns true if successful, false with error message if failed. ```lua local proc = process.open("sleep 60") -- ... do something ... -- Kill the process local success, error = proc:kill() if success then print("Process killed successfully") else print("Failed to kill process:", error) end proc:close() ``` Kill long-running process: ```lua local proc = process.open("xmake l os.sleep 60000") print("Process started:", proc) -- Kill after 2 seconds os.sleep(2000) local success = proc:kill() print("Kill result:", success) proc:close() ``` ## process:close * Close the subprocess #### Function Prototype ::: tip API ```lua process:close() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Closes the subprocess and releases associated resources. Should be called when done with the process. ```lua local proc = process.open("echo hello") proc:wait() local success = proc:close() print("Close result:", success) ``` Always close processes: ```lua local proc = process.open("some command") local ok, status = proc:wait() -- Always close, even if process failed proc:close() ``` ## process:name * Get the process name #### Function Prototype ::: tip API ```lua process:name() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the name of the process (filename without path). ```lua local proc = process.open("xmake lua print 'hello'") print("Process name:", proc:name()) -- Output: xmake proc:close() ``` ## process:program * Get the process program path #### Function Prototype ::: tip API ```lua process:program() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the full program path that was used to start the process. ```lua local proc = process.openv("xmake", {"lua", "print", "hello"}) print("Program:", proc:program()) -- Output: xmake proc:close() ``` ## process:cdata * Get the process cdata #### Function Prototype ::: tip API ```lua process:cdata() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the underlying cdata object for the process. Used internally by the scheduler and other low-level operations. ```lua local proc = process.open("echo hello") local cdata = proc:cdata() print("CData type:", type(cdata)) proc:close() ``` ## process:otype * Get the object type #### Function Prototype ::: tip API ```lua process:otype() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the object type identifier. For subprocess objects, this returns 3 (poller.OT\_PROC). ```lua local proc = process.open("echo hello") print("Object type:", proc:otype()) -- Output: 3 proc:close() ``` ::: tip TIP The process module is the underlying implementation for `os.exec` and `os.execv` functions. It provides more control and flexibility for process management, including timeout handling, pipe integration, and scheduler support. Use `process.open` for simple command execution and `process.openv` for safer argument handling. ::: --- --- url: /zh/api/scripts/extension-modules/core/base/process.md --- # process process 模块提供了子进程管理功能,用于创建、控制和与外部进程通信。这是 `os.exec` 和 `os.execv` 函数的底层模块。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.process")` ::: ## process.open * 使用命令字符串打开子进程 #### 函数原型 ::: tip API ```lua process.open(command: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | command | 必需。要执行的命令字符串 | | opt | 可选。进程选项表 | #### 用法说明 通过执行命令字符串创建新的子进程。返回一个子进程对象,可用于控制和与进程通信。 `opt` 中的选项: * `stdin` - 输入源(文件路径、文件对象或管道对象) * `stdout` - 输出目标(文件路径、文件对象或管道对象) * `stderr` - 错误输出目标(文件路径、文件对象或管道对象) * `envs` - 环境变量数组(例如:`{"PATH=xxx", "XXX=yyy"}`) ```lua -- 基本进程执行 local proc = process.open("echo hello world") local ok, status = proc:wait() proc:close() -- 带文件重定向的进程 local stdout = os.tmpfile() local stderr = os.tmpfile() local proc = process.open("xmake lua print 'hello'", { stdout = stdout, stderr = stderr }) proc:wait() proc:close() -- 从文件读取输出 local output = io.readfile(stdout):trim() print(output) -- 输出: hello ``` 带环境变量的进程: ```lua local proc = process.open("echo $MY_VAR", { envs = {"MY_VAR=hello from xmake"} }) proc:wait() proc:close() ``` ## process.openv * 使用程序和参数列表打开子进程 #### 函数原型 ::: tip API ```lua process.openv(program: , argv:
, opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | program | 必需。要执行的程序 | | argv | 必需。传递给程序的参数数组 | | opt | 可选。进程选项表(与 `process.open` 相同) | #### 用法说明 通过执行程序和参数列表创建新的子进程。这比 `process.open` 更安全,因为它避免了 shell 解释问题。 ```lua -- 使用参数执行程序 local proc = process.openv("xmake", {"lua", "print", "hello world"}) local ok, status = proc:wait() proc:close() -- 带文件重定向执行 local stdout = os.tmpfile() local proc = process.openv("xmake", {"lua", "print", "xmake"}, { stdout = stdout, stderr = stderr }) proc:wait() proc:close() -- 读取输出 local output = io.readfile(stdout):trim() print(output) -- 输出: xmake ``` 带环境变量执行: ```lua local proc = process.openv("env", {"MY_VAR=test"}, { envs = {"MY_VAR=hello from xmake"} }) proc:wait() proc:close() ``` ## process:wait * 等待子进程完成 #### 函数原型 ::: tip API ```lua process:wait(timeout: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | timeout | 可选。超时时间(毫秒)。使用 -1 表示无限等待,0 表示非阻塞 | #### 返回值说明 | 类型 | 描述 | |------|------| | ok | 退出代码(0 表示成功,负数表示错误) | | status | 进程状态或错误消息 | #### 用法说明 等待子进程完成并返回退出状态。可以带或不带超时使用。 ```lua local proc = process.open("echo hello") local ok, status = proc:wait() print("退出代码:", ok) -- 输出: 0 (成功) print("状态:", status) -- 输出: nil 或错误消息 proc:close() ``` 带超时等待: ```lua local proc = process.open("sleep 10") local ok, status = proc:wait(1000) -- 最多等待 1 秒 if ok < 0 then print("进程超时或失败:", status) end proc:close() ``` 非阻塞等待: ```lua local proc = process.open("echo hello") local ok, status = proc:wait(0) -- 非阻塞 if ok < 0 then print("进程尚未就绪") else print("进程完成,代码:", ok) end proc:close() ``` ## process:kill * 终止子进程 #### 函数原型 ::: tip API ```lua process:kill() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 立即终止子进程。成功返回 true,失败返回 false 和错误消息。 ```lua local proc = process.open("sleep 60") -- ... 做一些事情 ... -- 终止进程 local success, error = proc:kill() if success then print("进程终止成功") else print("终止进程失败:", error) end proc:close() ``` 终止长时间运行的进程: ```lua local proc = process.open("xmake l os.sleep 60000") print("进程已启动:", proc) -- 2 秒后终止 os.sleep(2000) local success = proc:kill() print("终止结果:", success) proc:close() ``` ## process:close * 关闭子进程 #### 函数原型 ::: tip API ```lua process:close() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 关闭子进程并释放相关资源。完成进程操作后应调用此方法。 ```lua local proc = process.open("echo hello") proc:wait() local success = proc:close() print("关闭结果:", success) ``` 始终关闭进程: ```lua local proc = process.open("some command") local ok, status = proc:wait() -- 即使进程失败也要关闭 proc:close() ``` ## process:name * 获取进程名称 #### 函数原型 ::: tip API ```lua process:name() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回进程的名称(不带路径的文件名)。 ```lua local proc = process.open("xmake lua print 'hello'") print("进程名称:", proc:name()) -- 输出: xmake proc:close() ``` ## process:program * 获取进程程序路径 #### 函数原型 ::: tip API ```lua process:program() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回用于启动进程的完整程序路径。 ```lua local proc = process.openv("xmake", {"lua", "print", "hello"}) print("程序:", proc:program()) -- 输出: xmake proc:close() ``` ## process:cdata * 获取进程 cdata #### 函数原型 ::: tip API ```lua process:cdata() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回进程的底层 cdata 对象。由调度器和其他低级操作内部使用。 ```lua local proc = process.open("echo hello") local cdata = proc:cdata() print("CData 类型:", type(cdata)) proc:close() ``` ## process:otype * 获取对象类型 #### 函数原型 ::: tip API ```lua process:otype() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回对象类型标识符。对于子进程对象,返回 3(poller.OT\_PROC)。 ```lua local proc = process.open("echo hello") print("对象类型:", proc:otype()) -- 输出: 3 proc:close() ``` ::: tip 提示 process 模块是 `os.exec` 和 `os.execv` 函数的底层实现。它提供了更多的控制和灵活性用于进程管理,包括超时处理、管道集成和调度器支持。使用 `process.open` 进行简单的命令执行,使用 `process.openv` 进行更安全的参数处理。 ::: --- --- url: /api/description/project-target.md --- # Project Targets 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. :::tip NOTE 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") ``` :::tip NOTE \`target()' interface can be repeatedly invoked in different places to set the same target. ::: ## Visibility Settings {#visibility} In xmake, many interfaces support `visibility` configuration parameters to control the visibility scope of configurations. The `visibility` parameter accepts an object containing the following key-value pairs: | Key | Description | Example | |-----|-------------|---------| | `public` | Export to dependent sub-targets, when other targets depend on this target, they will inherit these configurations | `{public = true}` | | `interface` | Export as interface, only effective for other targets that depend on this target, not effective for the current target itself | `{interface = true}` | | `private` | Only effective for the current target, will not be passed to other targets that depend on this target | `{private = true}` | ### Usage Examples ```lua -- Set public visibility, will be passed to other targets that depend on this target add_defines("PUBLIC_DEFINE", {public = true}) -- Set interface visibility, only effective for other targets that depend on this target add_includedirs("include", {interface = true}) -- Set private visibility, only effective for the current target add_defines("PRIVATE_DEFINE", {private = true}) ``` ### Default Behavior If the `visibility` parameter is not specified, most interfaces use `private` visibility by default, meaning configurations are only effective for the current target. ## target ### Define a project target #### Function Prototype ::: tip API ```lua target(name: , { kind = , files = , ... }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Target name string, used to identify the project target | | kind | Target type string, optional values: binary, static, shared, object, headeronly, phony | | files | Source file path string or array, supports wildcard matching patterns | | ... | Other configuration options, such as deps, defines, includedirs, etc. | #### Usage 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")` repeatedly to enter the target scope for modifying it's configuration. ```lua -- defines demo and enter it's scope to set configuration 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") ``` :::tip NOTE All configuration in root scope affects all targets, but does not affect the configuration 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 #### Function Prototype ::: tip API ```lua target_end() ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | No parameters | This interface does not require any parameters | #### Usage 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") ... ``` ## set\_kind ### Set target kind #### Function Prototype ::: tip API ```lua set_kind(kind: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | kind | Target type string, specifies the type of compilation target | #### Usage Set the target type, currently supported types are: | Value | Description | | ------ | ----------- | | phony | Phony target program | | binary | binary program | | static | Static library program | | shared | Dynamic library program | | object | Only compile a collection of objects | | headeronly | header file collection only | #### binary * Executable file type ```lua target("demo") set_kind("binary") add_files("src/*.c") ``` :::tip NOTE Starting from 2.5.5, if the set\_kind interface is not set, the default is binary type. ::: So we simplify to: ```lua target("demo") add_files("src/*.c") ``` even: ```lua target("demo", {files = "src/*.c"}) ``` #### static * Static library target type ```lua target("demo") set_kind("static") add_files("src/*.c") ``` #### shared * Dynamic library target type ```lua target("demo") set_kind("shared") add_files("src/*.c") ``` #### object * Pure object file list type Usually used between two target programs, part of the object file is shared, and only compiled once. It can also be used to separate the object file list and configure different compilation parameters. #### phony * Empty target type It is a special target program type. It does not generate any actual program files, but is only used to combine the dependencies of other target programs. ```lua target("test1") set_kind("binary") add_files("src/*.c") target("test2") set_kind("binary") add_files("src/*.c") target("demo") set_kind("phony") add_deps("test1", "test2") ``` For example, with the above configuration, we can compile two dependent programs at the same time: test1 and test2 when executing `xmake build demo`. #### headeronly * Pure header file target type After 2.5.9, we added the `headeronly` target type. For target programs of this type, we will not actually compile them because it has no source files to be compiled. But it contains a list of header files, which are usually used for the installation of headeronly library projects, the generation of file lists for IDE projects, and the generation of cmake/pkgconfig import files during the installation phase. E.g: ```lua add_rules("mode.release", "mode.debug") target("foo") set_kind("headeronly") add_headerfiles("src/foo.h") add_rules("utils.install.cmake_importfiles") add_rules("utils.install.pkgconfig_importfiles") ``` For more details, please see: [#1747](https://github.com/xmake-io/xmake/issues/1747) ## set\_strip ### Strip target symbols #### Function Prototype ::: tip API ```lua set_strip(strip: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | strip | Strip mode string, optional values: debug, all | #### Usage 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") ``` :::tip NOTE This api does not have to be used after the target. If no target is specified, it will be set to global mode. . ::: ## set\_enabled ### Enable or disable target #### Function Prototype ::: tip API ```lua set_enabled(enabled: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | enabled | Whether to enable the target, true means enabled, false means disabled | #### Usage If `set_enabled(false)` is set, the corresponding target will be directly disabled, including target loading and information acquisition, while [set\_default](#set_default) is just set to not compile by default, but the target can still get related information. , the default will also be loaded. ## set\_default ### Mark as default target #### Function Prototype ::: tip API ```lua set_default(default: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | default | Whether to mark as default build target, true means default build, false means not default build | #### Usage 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. :::tip NOTE 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: ```sh $ 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: ```sh $ xmake build [-a|--all] $ xmake install [-a|--all] ``` ## set\_options ### Set configuration options #### Function Prototype ::: tip API ```lua set_options(options: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | options | Option name string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple option name strings | #### Usage Add option dependencies. If you have customized some options through the [option](/api/description/configuration-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") ``` :::tip NOTE Some settings defined in [option](/api/description/configuration-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. ::: ## set\_symbols ### Set symbol info #### Function Prototype ::: tip API ```lua set_symbols(symbols: , { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | symbols | Symbol mode string, optional values: debug, hidden, none | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage 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 | gcc/clang | msvc | | ------ | ---------------------- | ----- | ---- | | debug | Add debugging symbols | -g | /Zi /Pdxxx.pdb | | debug, edit | Only for msvc, used with debug level | Ignore | /ZI /Pdxxx.pdb | | debug, embed | Only for msvc, used with debug level | Ignore | /Z7 | | hidden | Set symbol invisible | -fvisibility=hidden | Ignore | 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. . :::tip NOTE In v2.3.3 and above, you can automatically generate independent debugging symbols by setting at the same time with `set_strip("all")`. For example, for iOS programs, it is a .dSYM file, for Android and other programs, it is .sym Symbol file. ::: If target sets both of the following settings, symbol file generation will be enabled ```lua target("test")     set_symbols("debug")     set_strip("all") ``` For the built-in release mode, symbol generation is not enabled by default, it is just the strip targetfile. If you want to enable it, you only need to enable the debug symbol, because mode.release internally has strip enabled by default. ```lua add_rules("mode.release") target("test")     set_symbols("debug") ``` The ios program will generate a .dSYM file, and then Strip itself symbol ```sh [62%]: linking.release libtest.dylib [62%]: generating.release test.dSYM ``` The android program will generate a .sym file (actually a symbolic so/binary program), and then strip itself ```sh [62%]: linking.release libtest.so [62%]: generating.release test.sym ``` In v2.3.9 and above, two additional symbol levels, `edit` and `embed` have been added, which need to be combined with `debug` levels to further subdivide the debugging symbol format of the msvc compiler, for example: ```lua set_symbols("debug", "edit") ``` It will switch from the default `-Zi -Pdxxx.pdb` to `-ZI -Pdxxx.pdb` compilation option, enable `Edit and Continue` debugging symbol format information, of course, this will not affect the processing of gcc/clang, so it is Fully compatible. ## set\_basename ### Set the base name of target file #### Function Prototype ::: tip API ```lua set_basename(basename: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | basename | Target file base name string | #### Usage 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](#set_targetdir). Or implement more advanced logic by writing custom scripts, see: [after\_build](#after_build) and [os.mv](/api/scripts/builtin-modules/os#os-mv). ## set\_filename ### Set the full name of target file #### Function Prototype ::: tip API ```lua set_filename(filename: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | filename | Target file full name string, including prefix and suffix | #### Usage The difference between it and [set\_basename](#set_basename) is that [set\_basename](#set_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](#set_basename). ## set\_prefixname ### Set the leading name of the target file #### Function Prototype ::: tip API ```lua set_prefixname(prefixname: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | prefixname | Target file prefix name string, such as "lib" or "" | #### Usage Only supported after version 2.5.5, you can modify the prefix name of the target file, for example, change the default: `libtest.so` to `test.so` ```lua target("test") set_prefixname("") ``` ## set\_suffixname ### Set the postname of the target file #### Function Prototype ::: tip API ```lua set_suffixname(suffixname: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | suffixname | Target file suffix name string, such as "-d" or "" | #### Usage Only supported after version 2.5.5, you can modify the postname of the target file, for example, change the default: `libtest.so` to `libtest-d.so` ```lua target("test") set_suffixname("-d") ``` ## set\_extension ### Set the extension of the target file #### Function Prototype ::: tip API ```lua set_extension(extension: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | extension | Target file extension string, such as ".dll" or ".so" | #### Usage Only supported after version 2.5.5, you can modify the extension of the set target file, for example, change the default: `libtest.so` to `test.dll` ```lua target("test") set_prefixname("") set_extension(".dll") ``` ## set\_warnings ### Set compilation warning level #### Function Prototype ::: tip API ```lua set_warnings(warnings: , { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | warnings | Warning level string, optional values: none, less, more, all, error | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage 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 | | extra | Enable extra warnings | -Wextra | | | pedantic | Enable non-standard warnings | -Wpedantic | | | all | Enable all warnings | -Wall | -W3 | | allextra | Enable all warnings + extra warnings | -Wall -Wextra | -W4 | | 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. . ## set\_optimize ### Set competition optimization level #### Function Prototype ::: tip API ```lua set_optimize(optimize: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | optimize | Optimization level string, optional values: none, fast, faster, fastest, smallest, aggressive | #### Usage 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 | -O2 | | fastest | Optimization of the fastest running speed | -O3 | -Ox -fp:fast | | smallest | Minimize code optimization | -Os | -O1 -GL | | aggressive | over-optimization | -Ofast | -Ox -fp:fast | E.g: ```lua -- Optimization of the fastest running speed set_optimize("fastest") ``` ## set\_languages ### Set source code language standards #### Function Prototype ::: tip API ```lua set_languages(languages: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | languages | Language standard string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple language standard strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage 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 |C language standard| |----------|-------------------| | `ansi` |`ansi` | | `c89` |`c89` | | `gnu89` |`gnu89` | | `c90` |`c90` | | `gnu90` |`gnu90` | | `c99` |`c99` | | `gnu99` |`gnu99` | | `c11` |`c11` | | `c17` |`c17` | | `clatest`|`clatest` | |Value |C++ language standard | |-------------|----------------------| |`cxx98` |`c++98` | |`gnuxx98` |`gnu++98` | |`cxx03` |`c++03` | |`gnuxx03` |`gnu++03` | |`cxx11` |`c++11` | |`gnuxx11` |`gnu++11` | |`cxx14` |`c++14` | |`gnuxx14` |`gnu++14` | |`cxx1z` |`c++1z` | |`gnuxx1z` |`gnu++1z` | |`cxx17` |`c++17` | |`gnuxx17` |`gnu++17` | |`cxx20` |`c++20` | |`gnuxx20` |`gnu++20` | |`cxx2a` |`c++2a` | |`gnuxx2a` |`gnu++2a` | |`cxx23` |`c++23` | |`gnuxx23` |`gnu++23` | |`cxx2b` |`c++2b` | |`gnuxx2b` |`gnu++2b` | |`cxxlatest` |`c++latest` | |`gnuxxlatest`|`gnu++latest` | 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") ``` It is not that a specified standard is set, and the compiler will compile according to this standard. After all, each compiler supports different strengths, but xmake will try its best to adapt to the support standards of the current compilation tool. The msvc compiler does not support compiling c code according to the c99 standard, and can only support c89, but xmake supports it as much as possible, so after setting the c99 standard, xmake will force the c++ code mode to compile c code , To a certain extent, it solves the problem of compiling c99 c code under windows. . The user does not need to make any additional changes. However, the latest msvc compilation already supports the c11/c17 standard, and xmake will not do additional special processing. ## set\_fpmodels ### Set float-point compilation mode #### Function Prototype ::: tip API ```lua set_fpmodels(fpmodels: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | fpmodels | Float-point mode string or array, optional values: fast, strict, except, precise | | ... | Variable parameters, can pass multiple float-point mode strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage This interface is used to set the floating-point compilation mode and the compilation abstract settings for mathematical calculation related optimizations. It provides several commonly used levels such as fast, strict, except, precise, etc. Some of them can be set at the same time, and some are conflicting. Effective. For the description of these levels, you can refer to the Microsoft document: \[Specify floating-point behavior]\(https://docs.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior ?view=vs-2019) Of course, for other compilers such as gcc/icc, xmake will map to different compilation flags. ```lua set_fpmodels("fast") set_fpmodels("strict") set_fpmodels("fast", "except") set_fpmodels("precise") - default ``` For details about this, see: ## 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: ```sh 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") ``` :::tip NOTE If the display sets `set_targetdir`, then the directory specified by `set_targetdir` is preferred as the output directory of the target file. ::: Starting from 3.0, we can also configure the subdirectories of the build output such as bindir, libdir, includedir, etc., for example: ```lua target("test") set_kind("shared") add_files("src/x.cpp") set_targetdir("$(builddir)/out", { bindir = "bin", libdir = "lib" }) ``` ## 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("$(builddir)/.objs") ``` ## 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("$(builddir)/.deps") ``` ## 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](#on_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) ``` ## add\_rules ### Add custom compilation rule to target #### Function Prototype ::: tip API ```lua add_rules(rules: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | rules | Rule name string or array, such as "markdown" | | ... | Variable parameters, can pass multiple rule name strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage 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(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 send arguments to rule in add\_rules: ```lua rule("my_rule") on_load(function (target) local my_arg = extraconf("rules", "my_rule", "my_arg") -- "my arg" end) target("test") add_rules("my_rule", { my_arg = "my arg"}) ``` We can also specify the application of local files to the rules, see: [add\_files](#add_files). ## on\_load ### Run custom load target configuration script #### Function Prototype ::: tip API ```lua on_load(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Load script function, receives target parameter | #### Usage 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) add("defines", "DEBUG", "TEST=\"hello\"") add("linkdirs", "/usr/lib", "/usr/local/lib") add({includedirs = "/usr/include", "links" = "pthread"}) end) ``` You can dynamically add various target attributes in `on_load` via `set`, `add`. ## on\_config ### custom configuration script #### Function Prototype ::: tip API ```lua on_config(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Configuration script function, receives target parameter | #### Usage After `xmake config` is executed, this script is executed before Build, which is usually used for configuration work before compilation. It differs from on\_load in that on\_load is executed as soon as the target is loaded, and the execution timing is earlier. If some configuration cannot be configured prematurely in on\_load, it can be configured in on\_config. In addition, its execution time is earlier than before\_build, and the approximate execution flow is as follows: ``` on_load -> after_load -> on_config -> before_build -> on_build -> after_build ``` ## on\_prepare ### Run custom prepare phase script #### Function Prototype ::: tip API ```lua on_prepare(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Prepare phase script function, receives target, opt parameters | #### Usage Added in 3.0, the on\_prepare phase enables two-stage builds. The prepare phase is dedicated to source-level preprocessing, code generation, and source dependency analysis, before entering the build phase. on\_prepare scripts are called before all build scripts. You can use this to do preparation work, such as scanning C++ module source files, autogenerating code, etc. ```lua rule("scan_module_files") on_prepare(function (target, opt) -- scan module files end) ``` on\_prepare also supports the jobgraph parameter for parallel task scheduling: ```lua rule("scan_module_files") on_prepare(function (target, jobgraph, opt) jobgraph:add(target:name() .. "/scanfiles", function (index, total, opt) -- scan module files end) end, {jobgraph = true}) ``` ## on\_prepare\_file ### Run custom prepare phase single file script #### Function Prototype ::: tip API ```lua on_prepare_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Single file processing script function, receives target, sourcefile, opt parameters | #### Usage Through this interface, you can hook the built-in prepare phase process to preprocess, analyze, or generate code for each source file during the prepare phase. ```lua target("test") set_kind("binary") add_files("src/*.c") on_prepare_file(function (target, sourcefile, opt) -- process single source file end) ``` ## on\_prepare\_files ### Run custom prepare phase batch files script #### Function Prototype ::: tip API ```lua on_prepare_files(script: , {jobgraph = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Batch file processing script function, receives target, jobgraph, sourcebatch, opt parameters | | jobgraph | Whether to enable parallel task processing, optional values: true, false | #### Usage Through this interface, you can hook the built-in prepare phase process to batch preprocess, analyze, or generate code for a group of source files of the same type during the prepare phase. Supports jobgraph for parallel tasks. Usually used with set\_extensions to process specific file types in batch: ```lua rule("scan_module_files") set_extensions("*.mpp") on_prepare_files(function (target, jobgraph, sourcebatch, opt) jobgraph:add(target:name() .. "/scanfiles", function (index, total, opt) -- batch scan module files end) end, {jobgraph = true}) ``` ## on\_link ### Run custom link target script #### Function Prototype ::: tip API ```lua on_link(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Link script function, receives target parameter | #### Usage 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) ``` ## on\_build ### Run custom build target script #### Function Prototype ::: tip API ```lua on_build(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Build script function, receives target parameter | #### Usage 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) ``` :::tip NOTE Once the build process is set for this target target, the default build process for xmake will no longer be executed. ::: ## on\_build\_file ### Run custom build single file script #### Function Prototype ::: tip API ```lua on_build_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Single file build script function, receives target, sourcefile, opt parameters | #### Usage 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) end) ``` 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](#before_build_file) and [target.after\_build\_file](#after_build_file) will be more convenient and you don't need to call it. Opt.origin\`. ## on\_build\_files ### Run custom build files script #### Function Prototype ::: tip API ```lua on_build_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Multi-file build script function, receives target, sourcebatch, opt parameters | #### Usage 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) end) ``` After setting this interface, the corresponding file in the source file list will not appear in the custom [target.on\_build\_file](#on_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 ## on\_clean ### Run custom clean files script #### Function Prototype ::: tip API ```lua on_clean(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Clean script function, receives target parameter | #### Usage 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(targetfile()) end) ``` Some target interfaces are described as follows: | target interface | description | | ----------------------------------- | -------------------------------------------- | | name() | Get the target name | | targetfile() | Get the target file path | | get("kind") | Get the build type of the target | | get("defines") | Get the macro definition of the target | | get("xxx") | Other target information set by the `set_/add_` interface can be obtained through this interface | | add("links", "pthread") | Add target settings | | set("links", "pthread", "z") | Override target settings | | deps() | Get all dependent targets of the target | | dep("depname") | Get the specified dependency target | | sourcebatches() | Get a list of all source files for the target | ## on\_package ### Run custom package target script #### Function Prototype ::: tip API ```lua on_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Package script function, receives target parameter | #### Usage 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("$(builddir)/.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) ``` ## on\_install ### Run custom install target file script #### Function Prototype ::: tip API ```lua on_install(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Install script function, receives target parameter | #### Usage 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) ``` ## on\_uninstall ### Run custom uninstall target file script #### Function Prototype ::: tip API ```lua on_uninstall(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Uninstall script function, receives target parameter | #### Usage 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) ``` ## on\_run ### Run custom run target script #### Function Prototype ::: tip API ```lua on_run(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Run script function, receives target parameter | #### Usage 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) ``` ## before\_prepare ### Run custom script before prepare phase #### Function Prototype ::: tip API ```lua before_prepare(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before prepare script function, receives target parameter | #### Usage It does not override the default prepare operation, just adds some custom actions before the prepare phase. ```lua target("test") before_prepare(function (target) print("before prepare") end) ``` ## before\_prepare\_file ### Run custom script before prepare phase single file #### Function Prototype ::: tip API ```lua before_prepare_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before prepare single file script function, receives target, sourcefile, opt parameters | #### Usage Does not override the default single file operation, just adds some custom actions before on\_prepare\_file. ```lua target("test") before_prepare_file(function (target, sourcefile, opt) print("before prepare file") end) ``` ## before\_prepare\_files ### Run custom script before prepare phase batch files #### Function Prototype ::: tip API ```lua before_prepare_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before prepare batch files script function, receives target, sourcebatch, opt parameters | #### Usage Does not override the default batch operation, just adds some custom actions before on\_prepare\_files. ```lua target("test") before_prepare_files(function (target, sourcebatch, opt) print("before prepare files") end) ``` ## before\_link ### Run custom script before linking target #### Function Prototype ::: tip API ```lua before_link(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before link script function, receives target parameter | #### Usage 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) ``` ## before\_build ### Run custom script before building target #### Function Prototype ::: tip API ```lua before_build(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build script function, receives target parameter | #### Usage It does not override the default build operation, just add some custom actions before building. ```lua target("test") before_build(function (target) print("") end) ``` ## before\_build\_file ### Run custom script before building single file #### Function Prototype ::: tip API ```lua before_build_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build single file script function, receives target, sourcefile, opt parameters | #### Usage 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) ``` ## before\_build\_files ### Run custom script before building files #### Function Prototype ::: tip API ```lua before_build_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build files script function, receives target, sourcebatch, opt parameters | #### Usage 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) ``` ## before\_clean ### Run custom script before cleaning target #### Function Prototype ::: tip API ```lua before_clean(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before clean script function, receives target parameter | #### Usage It does not override the default cleanup operation, just add some custom actions before cleaning. ```lua target("test") before_clean(function (target) print("") end) ``` ## before\_package ### Run custom script before packaging target #### Function Prototype ::: tip API ```lua before_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before package script function, receives target parameter | #### Usage It does not override the default packaging operation, just add some custom operations before packaging. ```lua target("test") before_package(function (target) print("") end) ``` ## before\_install ### Run custom script before installing target #### Function Prototype ::: tip API ```lua before_install(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before install script function, receives target parameter | #### Usage It does not override the default installation operation, just add some custom actions before installation. ```lua target("test") before_install(function (target) print("") end) ``` ## before\_uninstall ### Run custom script before uninstalling target #### Function Prototype ::: tip API ```lua before_uninstall(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before uninstall script function, receives target parameter | #### Usage It does not override the default uninstall operation, just add some custom actions before uninstalling. ```lua target("test") before_uninstall(function (target) print("") end) ``` ## before\_run ### Run custom script before running target #### Function Prototype ::: tip API ```lua before_run(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before run script function, receives target parameter | #### Usage It does not override the default run operation, just add some custom actions before running. ```lua target("test") before_run(function (target) print("") end) ``` ## after\_prepare ### Run custom script after prepare phase #### Function Prototype ::: tip API ```lua after_prepare(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After prepare script function, receives target parameter | #### Usage It does not override the default prepare operation, just adds some custom actions after the prepare phase. ```lua target("test") after_prepare(function (target) print("after prepare") end) ``` ## after\_prepare\_file ### Run custom script after prepare phase single file #### Function Prototype ::: tip API ```lua after_prepare_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After prepare single file script function, receives target, sourcefile, opt parameters | #### Usage Does not override the default single file operation, just adds some custom actions after on\_prepare\_file. ```lua target("test") after_prepare_file(function (target, sourcefile, opt) print("after prepare file") end) ``` ## after\_prepare\_files ### Run custom script after prepare phase batch files #### Function Prototype ::: tip API ```lua after_prepare_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After prepare batch files script function, receives target, sourcebatch, opt parameters | #### Usage Does not override the default batch operation, just adds some custom actions after on\_prepare\_files. ```lua target("test") after_prepare_files(function (target, sourcebatch, opt) print("after prepare files") end) ``` ## after\_link ### Run custom script after linking target #### Function Prototype ::: tip API ```lua after_link(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After link script function, receives target parameter | #### Usage 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) ``` ## after\_build ### Run custom script after building target #### Function Prototype ::: tip API ```lua after_build(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build script function, receives target parameter | #### Usage 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", targetfile()) end) ``` ## after\_build\_file ### Run custom script after building single file #### Function Prototype ::: tip API ```lua after_build_file(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build single file script function, receives target, sourcefile, opt parameters | #### Usage 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) ``` ## after\_build\_files ### Run custom script after building files #### Function Prototype ::: tip API ```lua after_build_files(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build files script function, receives target, sourcebatch, opt parameters | #### Usage 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) ``` ## after\_clean ### Run custom script after cleaning target #### Function Prototype ::: tip API ```lua after_clean(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After clean script function, receives target parameter | #### Usage 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("$(builddir)/otherfiles") end) ``` ## after\_package ### Run custom script after packaging target #### Function Prototype ::: tip API ```lua after_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After package script function, receives target parameter | #### Usage It does not override the default packaging operation, just add some custom operations after packaging. ```lua target("test") after_package(function (target) print("") end) ``` ## after\_install ### Run custom script after installing target #### Function Prototype ::: tip API ```lua after_install(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After install script function, receives target parameter | #### Usage It does not override the default installation operation, just add some custom actions after installation. ```lua target("test") after_install(function (target) print("") end) ``` ## after\_uninstall ### Run custom script after uninstalling target #### Function Prototype ::: tip API ```lua after_uninstall(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After uninstall script function, receives target parameter | #### Usage It does not override the default uninstall operation, just add some custom actions after uninstalling. ```lua target("test") after_uninstall(function (target) print("") end) ``` ## after\_run ### Run custom script after running target #### Function Prototype ::: tip API ```lua after_run(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After run script function, receives target parameter | #### Usage 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) ``` ## set\_pcheader ### Set pre-compiled c header file #### Function Prototype ::: tip API ```lua set_pcheader(header: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | header | C precompiled header file path string | #### Usage 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") ``` ## set\_pcxxheader ### Set pre-compiled c++ header file #### Function Prototype ::: tip API ```lua set_pcxxheader(header: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | header | C++ precompiled header file path string | #### Usage 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") ``` ## set\_pmheader ### Set pre-compiled objc header file #### Function Prototype ::: tip API ```lua set_pmheader(header: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | header | ObjC precompiled header file path string | #### Usage Xmake supports accelerating objc program compilation by precompiling header files. Currently supported compilers are: gcc, clang, and msvc. The usage is as follows: ```lua target("test") set_pmheader("header.h") ``` ## set\_pmxxheader ### Set pre-compiled objc++ header file #### Function Prototype ::: tip API ```lua set_pmxxheader(header: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | header | ObjC++ precompiled header file path string | #### Usage Xmake supports precompiled header files to speed up ObjC++ program compilation. Currently supported compilers are: gcc, clang, and msvc. The usage is as follows: ```lua target("test") set_pmxxheader("header.h") ``` ## add\_deps ### Add target dependencies #### Function Prototype ::: tip API ```lua add_deps(deps: , ..., { inherit = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | deps | Dependency target name string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple dependency target name strings | | inherit | Whether to inherit dependency target configuration, optional values: true (inherit), false (not inherit) | #### Usage 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. :::tip NOTE 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 ## add\_links ### Add link libraries #### Function Prototype ::: tip API ```lua add_links(links: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | links | Link library name string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple link library name strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add a link library for the current target, which is usually paired with [add\_linkdirs](#add_linkdirs). ```lua target("demo") -- Add a link to libtest.a, equivalent to -ltest add_links("test") -- Add link search directory add_linkdirs("$(builddir)/lib") ``` Starting with version 2.8.1, add\_links also supports adding the full path to the library, e.g. `add_links("/tmp/libfoo.a")`, explicitly specifying the library file. ## add\_syslinks ### Add system link libraries #### Function Prototype ::: tip API ```lua add_syslinks(syslinks: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | syslinks | System link library name string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple system link library name strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage This interface is similar to [add\_links](#add_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("$(builddir)/lib") ``` The above configuration, even if `add_syslinks` is set in advance, the final link order is still: `-la -lb -lpthread -lm -ldl` ## add\_linkorders ### Adjust link order #### Function Prototype ::: tip API ```lua add_linkorders(linkorders: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | linkorders | Link order string or array, such as "dep1", "dep2" | | ... | Variable parameters, can pass multiple link order strings | #### Usage This is a feature only supported by xmake 2.8.5 and later, and is mainly used to adjust the link order within the target. Since xmake provides `add_links`, `add_deps`, `add_packages`, `add_options` interfaces, you can configure targets, dependencies, links in packages and options. However, the link order between them was previously less controllable and could only be generated in a fixed order, which was a bit overwhelming for some complex projects. For more details and background see: [#1452](https://github.com/xmake-io/xmake/issues/1452) #### Sort links In order to more flexibly adjust the various link orders within the target, we have added the `add_linkorders` interface, which is used to configure various link orders introduced by the target, dependencies, packages, options, and link groups. For example: ```lua add_links("a", "b", "c", "d", "e") -- e -> b -> a add_linkorders("e", "b", "a") --e->d add_linkorders("e", "d") ``` add\_links is the configured initial link order, and then we configure two local link dependencies `e -> b -> a` and `e -> d` through add\_linkorders. xmake will internally generate a DAG graph based on these configurations, and use topological sorting to generate the final link sequence and provide it to the linker. Of course, if there is a circular dependency and a cycle is created, it will also provide warning information. #### Sorting links and link groups In addition, we can also solve the problem of circular dependencies by configuring link groups through `add_linkgroups`. And `add_linkorders` can also sort link groups. ```lua add_links("a", "b", "c", "d", "e") add_linkgroups("c", "d", {name = "foo", group = true}) add_linkorders("e", "linkgroup::foo") ``` If we want to sort link groups, we need to give each link group a name, `{name = "foo"}`, and then we can reference the configuration through `linkgroup::foo` in `add_linkorders`. Version 2.9.6 adds the as\_needed configuration item, which can be used to disable as\_needed. (Not configured by default, that is, enabled.) ```lua add_linkgroups("c", "d", {as_needed = false}) ``` The corresponding flags are as follows. ```sh -Wl,--no-as-needed c d -Wl,--as-needed ``` #### Sort links and frameworks We can also sort links and frameworks for macOS/iPhoneOS. ```lua add_links("a", "b", "c", "d", "e") add_frameworks("Foundation", "CoreFoundation") add_linkorders("e", "framework::CoreFoundation") ``` #### Complete example For a complete example, we can look at: ```lua add_rules("mode.debug", "mode.release") add_requires("libpng") target("bar") set_kind("shared") add_files("src/foo.cpp") add_linkgroups("m", "pthread", {whole = true}) target("foo") set_kind("static") add_files("src/foo.cpp") add_packages("libpng", {public = true}) target("demo") set_kind("binary") add_deps("foo") add_files("src/main.cpp") if is_plat("linux", "macosx") then add_syslinks("pthread", "m", "dl") end if is_plat("macosx") then add_frameworks("Foundation", "CoreFoundation") end add_linkorders("framework::Foundation", "png16", "foo") add_linkorders("dl", "linkgroup::syslib") add_linkgroups("m", "pthread", {name = "syslib", group = true}) ``` The complete project is at: [linkorders example](https://github.com/xmake-io/xmake/blob/master/tests/projects/c%2B%2B/linkorders/xmake.lua) ## add\_linkgroups ### Add link group #### Function Prototype ::: tip API ```lua add_linkgroups(linkgroups: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | linkgroups | Link group name string or array, such as "group1", "group2" | | ... | Variable parameters, can pass multiple link group name strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage This is a feature only supported by versions after xmake 2.8.5. This link group feature is currently mainly used for compilation on the Linux platform and only supports the gcc/clang compiler. It should be noted that the concept of link group in gcc/clang mainly refers to: `-Wl,--start-group` xmake is aligned and encapsulated, further abstracted, and is not only used to process `-Wl,--start-group`, but also `-Wl,--whole-archive` and `-Wl,-Bstatic` . Below we will explain them one by one. For more details, see: [#1452](https://github.com/xmake-io/xmake/issues/1452) #### --start-group support `-Wl,--start-group` and `-Wl,--end-group` are linker options for handling complex library dependencies, ensuring that the linker can resolve symbolic dependencies and successfully connect multiple libraries. In xmake, we can achieve this in the following way: ```lua add_linkgroups("a", "b", {group = true}) ``` It will generate the corresponding `-Wl,--start-group -la -lb -Wl,--end-group` link options. If there is a symbolic circular dependency between libraries a and b, no link error will be reported and the link can be successful. For unsupported platforms and compilations, it will fall back to `-la -lb` #### --whole-archive support `--whole-archive` is a linker option commonly used when dealing with static libraries. Its function is to tell the linker to include all object files in the specified static library into the final executable file, not just the object files that satisfy the current symbol dependencies. This can be used to ensure that all code for certain libraries is linked, even if they are not directly referenced in the current symbol dependencies. For more information, please refer to the gcc/clang documentation. In xmake, we can achieve this in the following way: ```lua add_linkgroups("a", "b", {whole = true}) ``` It will generate the corresponding `-Wl,--whole-archive -la -lb -Wl,--no-whole-archive` link options. For unsupported platforms and compilations, it will fall back to `-la -lb` Additionally, we can configure group/whole at the same time: ```lua add_linkgroups("a", "b", {whole = true, group = true}) ``` #### -Bstatic support `-Bstatic` is also an option for compilers (such as gcc) to instruct the compiler to use only static libraries and not shared libraries when linking. For more information, please refer to the gcc/clang documentation. In xmake, we can achieve this in the following way: ```lua add_linkgroups("a", "b", {static = true}) ``` It will generate the corresponding `-Wl,-Bstatic -la -lb -Wl,-Bdynamic` linkage options. ## add\_files ### Add source files #### Function Prototype ::: tip API ```lua add_files(files: , ..., { defines = , languages = , includedirs = , rules = , force = {$flags}, sourcekind = , $flags = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | files | File path string or file path array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple file path strings | | defines | Macro definition string or array, set compilation macro definitions for specified files | | languages | Language standard string, such as "c99", "c++11", etc. | | includedirs | Header file search directory string or array | | rules | Custom build rule name string | | force | Force compilation option object, disable automatic detection, can contain various compilation options | | sourcekind | Force specified source file type string, such as "cc", "cxx", etc. | | $flags | Various compilation and linking options, including cflags, cxflags, cxxflags, mflags, mxflags, mxxflags, scflags, asflags, gcflags, dcflags, rcflags, fcflags, zcflags, cuflags, culdflags, cugencodes, ldflags, arflags, shflags, etc. | #### Usage 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 | | .manifest | windows manifest file | | .dll | windows export file | | .ld/.lds | linker scripts file for gcc/clang | | .map/.ver | version script file for gcc/clang | 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. :::tip NOTE 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", {rules = "markdown"}) ``` After version 2.3.1, you can use the sourcekind parameter to force the use of the C or C++ compiler: ```lua add_files("*.c", {sourcekind = "cxx"}) -- force to compile as c++ add_files("*.cpp", {sourcekind = "cc"}) -- force to compile as c ``` 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"}}) ``` ## remove\_files ### Remove source files #### Function Prototype ::: tip API ```lua remove_files(files: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | files | File path string or file path array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple file path strings | #### Usage Through this interface, you can delete the specified file from the list of files added by the [add\_files](#add_files) interface, for example: ```lua target("test") add_files("src/*.c") remove_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](#add_files), filtering mode, and bulk removal. ```lua target("test") add_files("src/**.c") remove_files("src/test*.c") remove_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 `remove_files` are added and deleted sequentially according to the calling sequence, and deleted by `remove_files("src/subdir/*.c|xxx.c")` Batch file, And exclude `src/subdir/xxx.c` (that is, don't delete this file). Note: This interface is only available in version v2.6.3. The previous version was del\_files, which has been abandoned. If you want to be compatible with the previous version, you can solve it through the following configuration. ```lua remove_files = remove_files or del_files ``` ## remove\_headerfiles ### Remove the specified file from the preceding list of header files #### Function Prototype ::: tip API ```lua remove_headerfiles(headerfiles: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | headerfiles | Header file path string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple header file path strings | #### Usage Mainly used to remove files from the list of header files set by `add_headerfiles`, similar to `remove_files`. This interface is only provided in v2.6.3 version. ## add\_linkdirs ### Add link search directories #### Function Prototype ::: tip API ```lua add_linkdirs(linkdirs: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | linkdirs | Link library search directory string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple link library search directory strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Set the search directory of the link library. This interface is used as follows: ```lua target("test") add_linkdirs("$(builddir)/lib") ``` This interface is equivalent to gcc's `-Lxxx` link option. Generally, it is used together with [add\_links](#add_links). Of course, it can also be added directly through the [add\_ldflags](#add_ldflags) or [add\_shflags](#add_shflags) interface. It is also possible. :::tip NOTE 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. ::: ## add\_rpathdirs ### Add load search directories for dynamic libraries #### Function Prototype ::: tip API ```lua add_rpathdirs(rpathdirs: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | rpathdirs | Runtime library search directory string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple runtime library search directory strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage After [add\_linkdirs](#add_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("$(builddir)/lib") add_rpathdirs("$(builddir)/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("$(builddir)/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. :::tip NOTE 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` In addition, for gcc, `add_rpathdirs` defaults to runpath. If you want to configure it explicitly, use `-Wl,--enable-new-dtags`, `-Wl,--disable-new-dtags` to configure rpath. Or runpath We can specify it through additional parameters, `add_rpathdirs("xxx", {runpath = true})` For relevant background details, see: [#5109](https://github.com/xmake-io/xmake/issues/5109) After 2.9.4, we added `add_rpathdirs("xxx", {install_only = true})`, which can configure the installed rpath path separately. ## add\_includedirs ### Add include search directories #### Function Prototype ::: tip API ```lua add_includedirs(includedirs: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includedirs | Header file search directory string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple header file search directory strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Set the search directory for the header file. This interface is used as follows: ```lua target("test") add_includedirs("$(builddir)/include") ``` Of course, it can also be set directly through interfaces such as [add\_cxflags](#add_cxflags) or [add\_mxflags](#add_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("$(builddir)/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](#add_deps) ::: tip NOTE If you don't want it to be fixed in the project, you can set it by: xmake f --includedirs=xxx or xmake f --cxflags="-I/xxx". This manual setting has higher directory search priority. ::: :::tip NOTE The header file does not support pattern matching by default, and it is not recommended to do so. It is easy to introduce some unnecessary subdirectories, resulting in the interference of various header file reference conflicts, and it is more difficult to check if there is a problem. ::: If the user insists on doing this, it can be achieved by `add_includedirs(os.dirs(path.join(os.scriptdir(), "xxx/**")))`. ## add\_sysincludedirs ### Add system header file search directory #### Function Prototype ::: tip API ```lua add_sysincludedirs(includedirs: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includedirs | System header file search directory string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple system header file search directory strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage `add_includedirs` is usually used to add search directories for project header files. The introduction of some system library header files may trigger some internal warning messages, but these warnings may be unavoidable for users and cannot be fixed. Then, every time these warnings are displayed, it will interfere with the user. Therefore, gcc/clang provides `-isystem` to set the system header file search path. The header files set through this interface will suppress some warning messages to avoid disturbing users . msvc also provides the `/external:I` compilation option to set it, but it needs a higher version of msvc to support it. Therefore, xmake provides `add_sysincludedirs` to abstractly adapt and set the search path of system library header files. If the current compiler does not support it, it will automatically switch back to the `-I` compilation option. ```lua target("test") add_sysincludedirs("/usr/include") ``` The generated compilation options are as follows: ```sh -isystem /usr/include ``` In the case of the msvc compiler, it will be: ```sh /experimental:external /external:W0 /external:I /usr/include ``` :::tip NOTE In addition, the dependency package introduced with `add_requires()` will also use `-isystem` as the external system header file by default. ::: ## add\_defines ### Add macro definition #### Function Prototype ::: tip API ```lua add_defines(defines: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | defines | Macro definition string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple macro definition strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage ```lua add_defines("DEBUG", "TEST=0", "TEST2=\"hello\"") ``` Equivalent to setting the compile option: ``` -DDEBUG -DTEST=0 -DTEST2=\"hello\" ``` ## add\_undefines ### Add macro undefinition #### Function Prototype ::: tip API ```lua add_undefines(undefines: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | undefines | Macro definition name string or array, such as "DEBUG" | | ... | Variable parameters, can pass multiple macro definition name strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage ```lua add_undefines("DEBUG") ``` Equivalent to setting the compile option: `-UDEBUG` In the code is equivalent to: `#undef DEBUG` ## add\_cflags ### Add c compilation flags #### Function Prototype ::: tip API ```lua add_cflags(cflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cflags | C compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple C compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options only for c code ```lua add_cflags("-g", "-O2", "-DDEBUG") ``` :::tip WARN 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}) ``` ## add\_cxflags ### Add c/c++ compilation flags Add compilation options to c/c++ code at the same time ## add\_cxxflags ### Add c++ compilation flags #### Function Prototype ::: tip API ```lua add_cxxflags(cxxflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cxxflags | C++ compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple C++ compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options only to c++ code #### Add compiler-specific flags In version 2.7.3, we have improved all flags adding interfaces to specify flags only for specific compilers, e.g. ```lua add_cxxflags("clang::-stdlib=libc++") add_cxxflags("gcc::-stdlib=libc++") add_cxxflags("cl::/GR-") add_cxxflags("clang_cl::/GR-") ``` Or. ```lua add_cxxflags("-stdlib=libc++", {tools = "clang"}) add_cxxflags("-stdlib=libc++", {tools = "gcc"}) add_cxxflags("/GR-", {tools = {"clang_cl", "cl"}}) ``` :::tip NOTE Not just for compile flags, but also for link flags such as add\_ldflags, which also work. For link flags, the user must specify ::: if they want to target the C or C++ linker, such as "clang" for C and "clangxx" for C++. ## add\_mflags ### Add objc compilation flags #### Function Prototype ::: tip API ```lua add_mflags(mflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | mflags | ObjC compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple ObjC compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage 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}) ``` ## add\_mxflags ### Add objc/objc++ compilation flags #### Function Prototype ::: tip API ```lua add_mxflags(mxflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | mxflags | ObjC/ObjC++ compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple ObjC/ObjC++ compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Also add compile options to objc/objc++ code ```lua add_mxflAgs("-framework CoreFoundation") ``` ## add\_mxxflags ### Add objc++ compilation flags #### Function Prototype ::: tip API ```lua add_mxxflags(mxxflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | mxxflags | ObjC++ compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple ObjC++ compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options only to objc++ code ```lua add_mxxflags("-framework CoreFoundation") ``` ## add\_scflags ### Add swift compilation flags #### Function Prototype ::: tip API ```lua add_scflags(scflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | scflags | Swift compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple Swift compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options to swift code ```lua add_scflags("xxx") ``` ## add\_asflags ### Add asm compilation flags #### Function Prototype ::: tip API ```lua add_asflags(asflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | asflags | Assembly compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple assembly compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options to assembly code ```lua add_asflags("xxx") ``` ## add\_gcflags ### Add go compilation flags #### Function Prototype ::: tip API ```lua add_gcflags(gcflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | gcflags | Go compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple Go compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compile options to golang code ```lua add_gcflags("xxx") ``` ## add\_dcflags ### Add dlang compilation flags #### Function Prototype ::: tip API ```lua add_dcflags(dcflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | dcflags | D language compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple D language compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options to dlang code ```lua add_dcflags("xxx") ``` ## add\_rcflags ### Add rust compilation flags #### Function Prototype ::: tip API ```lua add_rcflags(rcflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | rcflags | Rust compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple Rust compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options to the rust code ```lua add_rcflags("xxx") ``` ## add\_fcflags ### Add fortran compilation flags #### Function Prototype ::: tip API ```lua add_fcflags(fcflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | fcflags | Fortran compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple Fortran compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options to the fortran code ```lua add_fcflags("xxx") ``` ## add\_zcflags ### Add zig compilation flags #### Function Prototype ::: tip API ```lua add_zcflags(zcflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | zcflags | Zig compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple Zig compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options to the zig code ```lua add_zcflags("xxx") ``` ## add\_cuflags ### Add cuda compilation flags #### Function Prototype ::: tip API ```lua add_cuflags(cuflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cuflags | CUDA compilation option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple CUDA compilation option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add compilation options to cuda code ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") ``` ## add\_culdflags ### Add cuda device link flags #### Function Prototype ::: tip API ```lua add_culdflags(culdflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | culdflags | CUDA device link option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple CUDA device link option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage 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") ``` ## add\_cugencodes ### Add gencode settings for cuda devices #### Function Prototype ::: tip API ```lua add_cugencodes(cugencodes: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cugencodes | CUDA device gencode setting string or array, such as "sm\_30", "sm\_50" | | ... | Variable parameters, can pass multiple CUDA device gencode setting strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage The `add_cugencodes()` interface is actually a simplified encapsulation of `add_cuflags("-gencode arch=compute_xx, code=compute_xx")` compilation flags settings. The actual flags mapping relationship corresponding to the internal parameter values is as follows: ```lua - compute_xx --> `-gencode arch=compute_xx,code=compute_xx` - sm_xx --> `-gencode arch=compute_xx,code=sm_xx` - sm_xx,sm_yy --> `-gencode arch=compute_xx,code=[sm_xx,sm_yy]` - compute_xx,sm_yy --> `-gencode arch=compute_xx,code=sm_yy` - compute_xx,sm_yy,sm_zz --> `-gencode arch=compute_xx,code=[sm_yy,sm_zz]` - native --> match the fastest cuda device on current host, eg. for a Tesla P100, `-gencode arch=compute_60,code=sm_60` will be added, if no available device is found, no `-gencode` flags will be added ``` E.g: ```lua add_cugencodes("sm_30") ``` Is equivalent to ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") add_culdflags("-gencode arch=compute_30,code=sm_30") ``` Is it more streamlined? This is actually an auxiliary interface for simplifying the setup. And if we set the native value, then xmake will automatically detect the cuda device of the current host, and then quickly match its corresponding gencode setting, and automatically append it to the entire build process. For example, if our host's current GPU is Tesla P100, and it can be automatically detected by xmake, then the following settings: ```lua add_cugencodes("native") ``` Equivalent to: ```lua add_cugencodes("sm_60") ``` ## add\_ldflags ### Add static library link flags #### Function Prototype ::: tip API ```lua add_ldflags(ldflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ldflags | Link option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple link option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add static link option ```lua add_ldflags("-L/xxx", "-lxxx") ``` While adding flags, argument with space is not allowed defaultly, use expand = false instead. ```lua -- add_ldflags("-L/my lib") ERROR: Invalid arguments add_ldflags({"-L/my lib"}, {expand = false}) -- OK ``` ## add\_arflags ### Add archive library flags #### Function Prototype ::: tip API ```lua add_arflags(arflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | arflags | Static library archive option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple static library archive option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Affect the generation of static libraries ```lua add_arflags("xxx") ``` ## add\_shflags ### Add dynamic library link flags #### Function Prototype ::: tip API ```lua add_shflags(shflags: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | shflags | Dynamic library link option string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple dynamic library link option strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Affect the generation of dynamic libraries ```lua add_shflags("xxx") ``` ## add\_options ### Add option dependencies This interface is similar to [set\_options](#set_options), the only difference is that this is an append option, and [set\_options](#set_options) overrides the previous settings each time. ## add\_packages ### Add package dependencies #### Function Prototype ::: tip API ```lua add_packages(packages: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | packages | Package name string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple package name strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage In the target scope, add integration package dependencies, for example: ```lua target("test") add_packages("zlib", "polarssl", "pcre", "mysql") ``` In this way, when compiling the test target, if the package exists, the macro definition, the header file search path, and the link library directory in the package will be automatically appended, and all the libraries in the package will be automatically linked. Users no longer need to call the [add\_links](#add_links), [add\_includedirs](#add_includedirs), [add\_ldflags](#add_ldflags) interfaces to configure the dependent library links. After v2.2.2, this interface also supports packages defined by [add\_requires](/api/description/global-interfaces#add-requires) in remote dependency management. ```lua add_requires("zlib", "polarssl") target("test") add_packages("zlib", "polarssl") ``` After v2.2.3, it also supports overwriting built-in links to control the actual linked libraries: ```lua -- By default, there will be links to ncurses, panel, form, etc. add_requires("ncurses") target("test") -- Display specified, only use ncurses a link library add_packages("ncurses", {links = "ncurses"}) ``` Or simply disable links and only use header files: ```lua add_requires("lua") target("test") add_packages("lua", {links = {}}) ``` ## add\_languages ### Add language standards #### Function Prototype ::: tip API ```lua add_languages(languages: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | languages | Language standard string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple language standard strings | #### Usage Similar to [set\_languages](#set_languages), the only difference is that this interface will not overwrite the previous settings, but append settings. ## add\_vectorexts ### Add vector extensions #### Function Prototype ::: tip API ```lua add_vectorexts(vectorexts: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | vectorexts | Vector extension instruction string or array, such as "mmx", "neon", "avx" | | ... | Variable parameters, can pass multiple vector extension instruction strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage Add extended instruction optimization options, currently supports the following extended instruction sets: ```lua add_vectorexts("mmx") add_vectorexts("neon") add_vectorexts("avx", "avx2", "avx512") add_vectorexts("sse", "sse2", "sse3", "ssse3", "sse4.2") ``` :::tip NOTE 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. ::: In 2.8.2, we added `all` configuration item has been added which can be used to turn on all extended directive optimisations where possible. ```lua add_vectorexts("all") ``` ## add\_frameworks ### Add frameworks #### Function Prototype ::: tip API ```lua add_frameworks(frameworks: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | frameworks | Framework name string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple framework name strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage 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](#add_mxflags) and [add\_ldflags](#add_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. ## add\_frameworkdirs ### Add framework search directories #### Function Prototype ::: tip API ```lua add_frameworkdirs(frameworkdirs: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | frameworkdirs | Framework search directory string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple framework search directory strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage For some third-party frameworks, it is impossible to find them only through [add\_frameworks](#add_frameworks). You also need to add a search directory through this interface. ```lua target("test") add_frameworks("MyFramework") add_frameworkdirs("/tmp/frameworkdir", "/tmp/frameworkdir2") ``` ## set\_toolset ### Set toolset #### Function Prototype ::: tip API ```lua set_toolset(toolname: , tool: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | toolname | Tool name string, such as "cc", "cxx", "ld", "ar" | | tool | Tool path string, such as "/usr/bin/gcc" | #### Usage Separate settings for a specific target to switch a compiler, linker, but we recommend using [set\_toolchains](#set_toolchains) to switch the overall tool chain of a target. Compared with set\_toolchains, this interface only switches a specific compiler or linker of the toolchain. :::tip NOTE This interface is only supported in versions above 2.3.4. The set\_toolchain/set\_tool interface before 2.3.4 will be gradually deprecated. The new interface is adopted and the usage is the same. ::: 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_toolset("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. :::tip NOTE 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. ::: 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 | | fc | fortran compiler | | sc | swift compiler | | rust | rust compiler | | strip | strip program | | ld | c/c++/asm/objc and other general executable program linker | | sh | c/c++/asm/objc and other general dynamic library linkers | | ar | c/c++/asm/objc and other general static library archivers | | dcld | dlang executable linker, rcld/gcld and similar | | dcsh | dlang dynamic library linker, rcsh/gcsh and similar | 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 set_toolset("cc", "gcc@$(projectdir)/tools/bin/Mipscc.exe") ``` ## set\_toolchains ### Set up the toolchain #### Function Prototype ::: tip API ```lua set_toolchains(toolchains: , ..., { vs = , plat = , arch = , clang = , gcc = , vs_sdkver = , vs_toolset = , ... = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | toolchains | Toolchain name string or array, such as "gcc", "clang", "msvc", "ndk\[gcc]", "mingw\[clang]@llvm-mingw", "msvc\[vs=2025]" | | vs | Visual Studio version, such as "2022", "2025" | | plat | Platform name, such as "android", "ios" | | arch | Architecture name, such as "arm64", "x64" | | clang | Use Clang compiler in MinGW toolchain, boolean value | | gcc | Use GCC compiler in Android NDK toolchain, boolean value | | vs\_sdkver | Visual Studio SDK version | | vs\_toolset | Visual Studio toolset version | | ... | Variable parameters, can pass multiple toolchain name strings | #### Usage This sets up different tool chains for a specific target individually. Unlike set\_toolset, this interface is an overall switch for a complete tool chain, such as cc/ld/sh and a series of tool sets. This is also a recommended practice, because most compiler tool chains like gcc/clang, the compiler and the linker are used together. To cut it, you have to cut it as a whole. Separate and scattered switch settings will be cumbersome. ### Toolchain Configuration Syntax Starting from xmake 3.0.5, you can use simplified syntax for toolchain configuration: ```lua -- Basic toolchain names set_toolchains("gcc", "clang", "msvc") -- With configuration options (new syntax) set_toolchains("ndk[gcc]") -- Use GCC in Android NDK set_toolchains("mingw[clang]@llvm-mingw") -- Use Clang in MinGW from llvm-mingw package set_toolchains("msvc[vs=2025]") -- Use Visual Studio 2025 -- Traditional configuration syntax set_toolchains("ndk", {gcc = true}) -- Use GCC in Android NDK set_toolchains("mingw", {clang = true}) -- Use Clang in MinGW (requires add_requires("llvm-mingw")) set_toolchains("msvc", {vs = "2025"}) set_toolchains("msvc", {vs = "2022", vs_sdkver = "10.0.19041.0"}) set_toolchains("ndk", {plat = "android", arch = "arm64", gcc = true}) ``` ### Package Dependencies When using `mingw[clang]@llvm-mingw` syntax, you need to add the package dependency: ```lua add_requires("llvm-mingw") set_toolchains("mingw[clang]@llvm-mingw") ``` Or with traditional syntax: ```lua add_requires("llvm-mingw") set_toolchains("mingw", {clang = true}) ``` ### Command Line Usage You can also specify toolchain configurations via command line: ```bash # Switch to GCC in Android NDK xmake f -p android --ndk=~/Downloads/android-ndk-r14b/ --toolchain=ndk[gcc] -c # Switch to Clang in MinGW (package dependency handled automatically) xmake f --toolchain=mingw[clang]@llvm-mingw -c # Switch to Visual Studio 2025 xmake f --toolchain=msvc[vs=2025] -c ``` For example, we switch the test target to two tool chains of clang+yasm: ```lua target("test")     set_kind("binary")     add_files("src/*.c")     set_toolchains("clang", "yasm") ``` You only need to specify the name of the toolchain. Specific toolchains supported by xmake can be viewed by the following command: ```sh $ xmake show -l toolchains xcode Xcode IDE vs VisualStudio IDE yasm The Yasm Modular Assembler clang A C language family frontend for LLVM go Go Programming Language Compiler dlang D Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit ndk Android NDK rust Rust Programming Language Compiler llvm A collection of modular and reusable compiler and toolchain technologies cross Common cross compilation toolchain nasm NASM Assembler gcc GNU Compiler Collection mingw Minimalist GNU for Windows gnu-rm GNU Arm Embedded Toolchain envs Environment variables toolchain fasm Flat Assembler ``` Of course, we can also switch to other tool chains globally through the command line: ```sh $ xmake f --toolchain=clang $ xmake ``` In addition, we can also customize toolchain in xmake.lua, and then specify it through `set_toolchains`, for example: ```lua toolchain("myclang")     set_kind("standalone")     set_toolset("cc", "clang")     set_toolset("cxx", "clang", "clang++")     set_toolset("ld", "clang++", "clang")     set_toolset("sh", "clang++", "clang")     set_toolset("ar", "ar")     set_toolset("ex", "ar")     set_toolset("strip", "strip")     set_toolset("mm", "clang")     set_toolset("mxx", "clang", "clang++")     set_toolset("as", "clang")     - ... ``` For details about this piece, you can go to the [Custom Toolchain](/api/description/custom-toolchain). For more details, please see: [#780](https://github.com/xmake-io/xmake/issues/780) Starting from version 2.3.5, new settings and switches for toolchains platform and architecture have been added, such as: ```lua target("test") set_toolchains("xcode", {plat = os.host(), arch = os.arch()}) ``` If it is currently in cross-compilation mode, this test will still be forced to switch to the local compilation toolchain of xcode and the corresponding pc platform. This is for those who want to support part of the target using the host toolchain and part of the target using the cross-compilation toolchain. ,very useful. However, this is not particularly convenient, especially when cross-platform compilation, pc tool chains of different platforms are different, there are msvc, xcode, clang, etc., you need to judge the platform to specify. Therefore, we can directly use the [set\_plat](#set_plat) and [set\_arch](#set_arch) interfaces to directly set a specific target to the host platform, and we can automatically select the host toolchain internally, for example: ```lua target("test") set_plat(os.host()) set_arch(os.arch()) ``` The application scenario and example of this piece can be seen: https://github.com/xmake-io/xmake-repo/blob/dev/packages/l/luajit/port/xmake.lua In luajit, you need to compile the minilua/buildvm of the host platform to generate jit related code, and then start compiling luajit itself to different cross tool chains. For details of this, you can refer to: https://github.com/xmake-io/xmake/pull/857 v2.5.1 has made further improvements to set\_toolchains to better support independent toolchain switching for specific targets. For example, different targets support switching to different VS versions, for example: ```lua target("test") set_toolchains("msvc", {vs = "2015"}) ``` By default, xmake will use the global vs tool chain. For example, if vs2019 is currently detected, but the user also installs vs2015 at the same time, you can switch the test target to vs2015 to compile through the above configuration. You can even use `set_arch` to specify a specific architecture to x86 instead of the default x64. ```lua target("test") set_arch("x86") set_toolchains("msvc", {vs = "2015"}) ``` The above effect is similar to `set_toolchains("msvc", {vs = "2015", arch = "x86"})`, but `set_arch` is for target granularity, and the arch setting in `set_toolchains` is only for specific tools Chain granularity. Generally, we recommend using `set_arch` to switch the architecture of the entire target. Since v3.0.4, for the mingw toolchain, we can specify a particular MSYS2 environment using the `msystem` parameter, for example: ```lua target("ucrt64") set_arch("x86_64") set_kind("binary") add_files("src/*.c") set_toolchains("mingw", {msystem = "ucrt64"}) ``` This allows xmake to use the mingw toolchain from the specified MSYS2 environment (ucrt64 in this case). Supported msystem values include: `mingw32`, `mingw64`, `ucrt64`, `clang64`, etc. ## set\_plat ### Set the compilation platform for the specified target #### Function Prototype ::: tip API ```lua set_plat(plat: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | plat | Platform name string, such as "linux", "macosx", "windows", "android" | #### Usage Usually used with [set\_arch](#set_arch) to switch the compilation platform of the specified target to the specified platform, xmake will automatically select the appropriate tool chain according to the switched platform. Generally used in scenarios where the host platform target and cross-compilation target need to be compiled at the same time. For more details, see: [set\_toolchains](#set_toolchains) E.g: ```sh $ xmake f -p android --ndk=/xxx ``` Even if you are using android ndk to compile the android platform target, the host target it depends on will still switch to the host platform and use xcode, msvc and other host tool chains to compile. ```lua target("host") set_kind("binary") set_plat(os.host()) set_arch(os.arch()) add_files("src/host/*.c") target("test") set_kind("binary") add_deps("host") add_files("src/test/*.c") ``` ## set\_arch ### Set the compilation architecture of the specified target #### Function Prototype ::: tip API ```lua set_arch(arch: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | arch | Architecture name string, such as "x86", "x64", "arm64", "armv7" | #### Usage For details, see: [set\_plat](#set_plat) ## set\_values ### Set custom configuration values #### Function Prototype ::: tip API ```lua set_values(name: , values: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration name string, such as "markdown\_flags" | | values | Configuration value, can be any type | | ... | Variable parameters, can pass multiple configuration values | #### Usage 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 = values("markdown.flags") if flags then -- .. end end) target("test") add_files("src/*.md", {rules = "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 `values("markdown.flags")`. :::tip NOTE 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](/api/description/builtin-rules). ::: The following is a list of some built-in extended configuration items currently supported by xmake. | Extended configuration name | Configuration description | | --- | --- | | fortran.moduledir | Set the output directory of the fortran module | | ndk.arm\_mode | Set the arm compilation mode of ndk (arm/thumb) | | objc.build.arc | Set to enable or disable objc's arc | | objc++.build.arc | Set to enable or disable arc of objc++ | | xcode.bundle\_identifier | Set the Bundle Identifier of the xcode toolchain | | xcode.mobile\_provision | Set the certificate information of the xcode toolchain | | xcode.codesign\_identity | Set the code signing identity of the xcode toolchain | | wasm.preloadfiles | Set the preload file (and path mapping) of wasm build | | wdk.env.winver | Set the win support version of wdk | | wdk.umdf.sdkver | Set the umdf sdk version of wdk | | wdk.kmdf.sdkver | Set the kmdf sdk version of wdk | | wdk.sign.mode | Set the code signing mode of wdk | | wdk.sign.store | Set wdk code signing store | | wdk.sign.certfile | Set wdk code signing certificate file | | wdk.sign.thumbprint | Set wdk code signing fingerprint | | wdk.sign.digest\_algorithm | Set wdk code signing digest algorithm | ## add\_values ### Add custom configuration values #### Function Prototype ::: tip API ```lua add_values(name: , values: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration name string, such as "markdown\_flags" | | values | Configuration value, can be any type | | ... | Variable parameters, can pass multiple configuration values | #### Usage Usage is similar to [set\_values](#set_values), the difference is that this interface is an additional setting, and will not override the settings each time. ## set\_rundir ### Set the running directory #### Function Prototype ::: tip API ```lua set_rundir(rundir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | rundir | Running directory path string | #### Usage 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") ``` ## set\_runargs ### Set the list of run parameters #### Function Prototype ::: tip API ```lua set_runargs(runargs: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | runargs | Run argument string or array, such as "-x", "--arg1=val" | | ... | Variable parameters, can pass multiple run argument strings | #### Usage 2.6.9 New interface to set default run arguments for `xmake run`, with which we can avoid typing run arguments every time on the command line, `xmake run -x --arg1=val` ```lua set_runargs("-x", "--arg1=val") ``` ## add\_runenvs ### Add runtime environment variables #### Function Prototype ::: tip API ```lua add_runenvs(name: , values: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Environment variable name string, such as "PATH", "LD\_LIBRARY\_PATH" | | values | Environment variable value string or array, supports multiple values | | ... | Variable parameters, can pass multiple environment variable values | #### Usage This interface is used to add an environment variable that sets the default running target program. Unlike [set\_runenv](#set_runenv), this interface appends the value in the existing system env and does not overwrite it. Therefore, for PATH, it is very convenient to append values through this interface, and this interface supports multi-value settings, so it is usually used to set multi-value env with path sep. . ```lua target("test")     set_kind("binary")     add_files("src/*.c")     add_runenvs("PATH", "/tmp/bin", "xxx/bin")     add_runenvs("LD_LIBRARY_PATH", "/tmp/lib", "xxx/lib") ``` ## set\_runenv ### Set the runtime environment variable #### Function Prototype ::: tip API ```lua set_runenv(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Environment variable name string, such as "PATH", "LD\_LIBRARY\_PATH" | | value | Environment variable value string | #### Usage This interface differs from [add\_runenvs](#add_runenvs) in that `set_runenv` is an override setting for an environment variable that overrides the env value of the original system environment, and this interface is singular and cannot pass multiple parameters. So, if you want to override the env that sets the multipath in PATH, you need to splicing yourself: ```lua target("test")     set_kind("binary")     add_files("src/*.c")     set_runenv("PATH", path.joinenv("/tmp/bin", "xxx/bin"))     set_runenv("NAME", "value") ``` ## set\_installdir ### Set the installation directory #### Function Prototype ::: tip API ```lua set_installdir(installdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | installdir | Installation directory path string | #### Usage 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. ## set\_prefixdir ### Set the installation prefix subdirectory #### Function Prototype ::: tip API ```lua set_prefixdir(prefixdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | prefixdir | Installation prefix subdirectory path string | #### Usage Although the installation root directory is set by `set_installdir` and `xmake install -o [installdir]`, if we still want to further adjust the subpaths of bin, lib and include. Then, we can use this interface. By default, the installation directory will follow this structure: ```sh installdir - bin - lib - include ``` If we configure: ```lua set_prefixdir("prefixdir") ``` It is to add a general subdirectory: ```sh installdir - prefixdir - bin - lib - include ``` We can also configure bin, lib and include subdirectories separately, for example: ```lua set_prefixdir("prefixdir", {bindir = "mybin", libdir = "mylib", includedir = "myinc"}) ``` ```sh installdir - prefixdir - mybin - mylib - myinc ``` If we do not configure prefixdir and only modify the bin subdirectory, we can configure prefixdir to `/`. ```lua set_prefixdir("/", {bindir = "mybin", libdir = "mylib", includedir = "myinc"}) ``` ```sh installdir - mybin - mylib - myinc ``` ## add\_installfiles ### Add installation files #### Function Prototype ::: tip API ```lua add_installfiles(installfiles: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | installfiles | Installation file path string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple installation file path strings | | prefixdir | Installation prefix directory, optional | | rootdir | Root directory, optional | | filename | Filename, optional | #### Usage 2.2.5 version of the new interface, used to set the corresponding file for each target, generally used for the `xmake install/uninstall` command. For example, we can specify to install various types of files to the installation directory: ```lua target("test")     add_installfiles("src/*.h")     add_installfiles("doc/*.md") ``` By default on Linux and other systems, we will install to `/usr/local/*.h, /usr/local/*.md`, but we can also specify to install to a specific subdirectory: ```lua target("test")     add_installfiles("src/*.h", {prefixdir = "include"})     add_installfiles("doc/*.md", {prefixdir = "share/doc"}) ``` The above settings, we will install to `/usr/local/include/*.h, /usr/local/share/doc/*.md` We can also install by subdirectory in the source file by `()`, for example: ```lua target("test")     add_installfiles("src/(tbox/*.h)", {prefixdir = "include"})     add_installfiles("doc/(tbox/*.md)", {prefixdir = "share/doc"}) ``` We extract the `src/*.h` subdirectory structure from the files in `src/tbox/*.h` and install it: `/usr/local/include/tbox/*.h, /usr/local /share/doc/tbox/*.md` Of course, users can also use the [set\_installdir](#set_installdir) interface. For a detailed description of this interface, see: https://github.com/xmake-io/xmake/issues/318 ## add\_headerfiles ### Add header files #### Function Prototype ::: tip API ```lua add_headerfiles(headerfiles: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | headerfiles | Header file path string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple header file path strings | | prefixdir | Installation prefix directory, optional | | rootdir | Root directory, optional | | filename | Filename, optional | #### Usage 2.2.5 version of the new interface, used to set the corresponding header file for each target, generally used for the `xmake install/uninstall` command. This interface is used in almost the same way as the [add\_installfiles](#add_installfiles) interface. But it is provided for installing header files. It is not required to set the `prefixdir` option. The header files are installed into the corresponding `include` subdirectory by default. And this interface for the `xmake project -k vs201x` and other plug-in generated IDE files, will also add the corresponding header file into it. We can also install by subdirectory in the source file by `()`, for example: ```lua target("test")     add_headerfiles("src/(tbox/*.h)", {prefixdir = "include"}) ``` After v2.7.1, we can disable the default header file installation behavior through the `{install = false}` parameter, and only display and edit the set header files for the project generator's file list, such as vs project. ```lua add_headerfiles("src/foo.h") add_headerfiles("src/test.h", {install = false}) ``` The above two header files will be displayed in the vs project, but only foo.h will be distributed and installed on the system. ## set\_configdir ### Set the output directory of configuration files #### Function Prototype ::: tip API ```lua set_configdir(configdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | configdir | Template configuration file output directory path string | #### Usage Version 2.2.5 adds a new interface, mainly used for the output directory of the template configuration file set by the [add\_configfiles](#add_configfiles) interface. ## set\_configvar ### Set template configuration variables #### Function Prototype ::: tip API ```lua set_configvar(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Configuration variable name string, such as "HAS\_FOO" | | value | Configuration variable value, can be any type | #### Usage The new interface in version 2.2.5 is used to add some template configuration variables that need to be pre-processed before compilation, generally used in the [add\_configfiles](#add_configfiles) interface. ```lua target("test") set_kind("binary") add_files("main.c") set_configvar("HAS_FOO", 1) set_configvar("HAS_BAR", "bar") set_configvar("HAS_ZOO", "zoo", {quote = false}) add_configfiles("config.h.in") ``` config.h.in ```c ${define HAS_FOO} ${define HAS_BAR} ${define HAS_ZOO} ``` The content of the generated config.h is as follows: ```c define HAS_FOO 1 define HAS_BAR "bar" define HAS_ZOO zoo ``` set\_configvar can set number, string and boolean type values. If it is a string value, the macro definition generated by default is enclosed in quotation marks. If you want to remove the quotation marks, you can set `{quote = false}`. For related issues, see: [#1694](https://github.com/xmake-io/xmake/issues/1694) If there is a path in the macro definition, and the path separator needs to be escaped, we can also configure to enable path character escaping. ```lua set_configvar("TEST", "C:\\hello", {escape = true}) ``` It will be automatically escaped into `#define TEST "C:\\hello"`, if escaping is not turned on, it will become: `#define TEST "C:\hello"` For related issues, see: [#1872](https://github.com/xmake-io/xmake/issues/1872) ## add\_configfiles ### Add template configuration files #### Function Prototype ::: tip API ```lua add_configfiles(configfiles: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | configfiles | Configuration file path string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple configuration file path strings | | prefixdir | Installation prefix directory, optional | | rootdir | Root directory, optional | | filename | Filename, optional | #### Usage 2.2.5 version of the new interface, used to add some configuration files that need to be pre-processed before compiling. Let's start with a simple example: ```lua target("test") set_kind("binary") add_files("src/*.c") set_configdir("$(builddir)/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](#add_configfiles), which also supports prefixdir and subdirectory extraction settings: ```lua add_configfiles("src/*.h.in", {prefixdir = "subdir"}) add_configfiles("src/(tbox/config.h)") ``` #### Variables 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", {onlycopy = true}) ``` The template variable is set via the [set\_configvar](#set_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 `{onlycopy = 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 = "@(.-)@"}) ``` #### Builtin variables 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 ``` Added git related built-in variables after v2.5.3: ```c define GIT_COMMIT "${GIT_COMMIT}" define GIT_COMMIT_LONG "${GIT_COMMIT_LONG}" define GIT_COMMIT_DATE "${GIT_COMMIT_DATE}" define GIT_BRANCH "${GIT_BRANCH}" define GIT_TAG "${GIT_TAG}" define GIT_TAG_LONG "${GIT_TAG_LONG}" define GIT_CUSTOM "${GIT_TAG}-${GIT_COMMIT}" ``` ```c define GIT_COMMIT "8c42b2c2" define GIT_COMMIT_LONG "8c42b2c251793861eb85ffdf7e7c2307b129c7ae" define GIT_COMMIT_DATE "20210121225744" define GIT_BRANCH "dev" define GIT_TAG "v1.6.6" define GIT_TAG_LONG "v1.6.6-0-g8c42b2c2" define GIT_CUSTOM "v1.6.6-8c42b2c2" ``` #### Macro definition 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 -> Add 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 #### Define export macros A new feature added in v2.9.8 is that it can generate export macro definitions for dynamic libraries, which are usually used for symbol export and import of dll libraries under Windows. Define in config.h.in: ```c ${define_export MYLIB} ``` It will generate ```c ifdef MYLIB_STATIC define MYLIB_EXPORT else if defined(_WIN32) define MYLIB_EXPORT __declspec(dllexport) elif defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) define MYLIB_EXPORT __attribute__((visibility("default"))) else define MYLIB_EXPORT endif endif ``` When we define the dynamic library export symbol, we can use this macro to control the import and export. ```c MYLIB_EXPORT void foo(); ``` It is similar to CMake's [GenerateExportHeader](https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html). However, it does not generate an independent export header file, but generates it directly in config.h. For more details, see: [#6088](https://github.com/xmake-io/xmake/issues/6088) #### Custom preprocessor If the built-in build rules of xmake do not meet your needs, you can also customize the processor to rewrite the build rules, such as rewriting `${define_export XXX}`: ```lua target("test") set_kind("binary") add_files("main.c") add_configfiles("config.h.in", { preprocessor = function (preprocessor_name, name, value, opt) if preprocessor_name == "define_export" then value = ([[#ifdef %s_STATIC define %s_EXPORT else if defined(_WIN32) define %s_EXPORT __declspec(dllexport) elif defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) define %s_EXPORT __attribute__((visibility("default"))) else define %s_EXPORT endif endif ]]):format(name, name, name, name, name) return value end end}) ``` We can also override the generation of `${define XXX}` and `${default XXX}`, or even customize and extend other preprocessor configurations. For example: ```lua target("test") set_kind("binary") add_files("main.c") set_configvar("FOO", "foo") add_configfiles("config.h.in", { preprocessor = function (preprocessor_name, name, value, opt) local argv = opt.argv if preprocessor_name == "define_custom" then return string.format("#define CUSTOM_%s %s", name, value) end end}) ``` Then we configure in config.h.in: ```c ${define_custom FOO arg1 arg2} ``` Where, `define_custom` is the custom preprocessor name, FOO is the variable name, and the variable value can be obtained from `set_configvar`. arg1 and arg2 are optional preprocessing parameter lists. Whether they are needed depends on actual needs. If you want to use parameters, you can get them through `opt.argv`, which is a parameter list table. After running `xmake config`, the following configuration will be automatically generated in config.h: ```c define CUSTOM_FOO foo ``` ## set\_policy ### Set build policy #### Function Prototype ::: tip API ```lua set_policy(policy: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | policy | Policy name string, such as "check.auto\_ignore\_flags", "build.warning" | | value | Policy value, true means enable, false means disable | #### Usage Xmake has many default behaviors, such as: automatic detection and mapping of flags, cross-target parallel construction, etc. Although it provides a certain amount of intelligent processing, it is difficult to adjust and may not meet all users' habits and needs. Therefore, starting with v2.3.4, xmake provides modified settings for the default build strategy, which is open to users to a certain degree of configurability. The usage is as follows: ```lua set_policy("check.auto_ignore_flags", false) ``` You only need to set this configuration in the project root domain to disable the automatic detection and ignore mechanism of flags. In addition, set\_policy can also take effect locally for a specific target. ```lua target ("test")     set_policy ("check.auto_ignore_flags", false) ``` For a complete list of policies support and instructions, see: [build policies](/api/description/builtin-policies). ## set\_runtimes ### Set the runtime library of the compilation target #### Function Prototype ::: tip API ```lua set_runtimes(runtimes: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | runtimes | Runtime library string or array, such as "MT", "MD", "MTd", "MDd" | | ... | Variable parameters, can pass multiple runtime library strings | #### Usage This is a newly added interface since v2.5.1, which is used to abstractly set the runtime library that the compilation target depends on. Currently, only the abstraction of the msvc runtime library is supported, but the mapping to other compiler runtime libraries may be expanded in the future. Some of the currently supported configuration values are described as follows: | Value | Description | | ------ | ----------------------------------------- | | MT | msvc runtime library: multithreaded static library | | MTd | msvc runtime library: multithreaded static library (debug) | | MD | msvc runtime library: multi-threaded dynamic library | | MDd | msvc runtime library: multi-threaded dynamic library (debug) | | c++\_static | clang's c++ runtime library, static library | | c++\_shared | c++ Runtime Library, Dynamic Libraries | | stdc++\_static | c++ runtime library for gcc, static library | | stdc++\_shared | c++ runtime library for gcc, dynamic libraries | | gnustl\_static | c++ runtime library for android, static libraries, deprecated in higher NDK versions | | gnustl\_shared | c++ runtime library, static library for android, deprecated in higher NDK versions | | stlport\_static | c++ runtime library, static library for android, deprecated by NDK | | stlport\_static | c++ Runtime Library for android, static library, deprecated in higher NDK versions | About vs runtime, you can refer to: \[msvc runtime description]\(https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view =msvc-160) And this interface passes in the MT/MTd parameter configuration, xmake will automatically configure the `/MT /nodefaultlib:msvcrt.lib` parameter. We can set different runtimes for different targets. In addition, if we set `set_runtimes` in the global root domain, then all `add_requires("xx")` package definitions will also be globally synchronized to the corresponding vs runtime configuration ```lua set_runtimes("MD") add_requires("libcurl", "fmt") target("test") set_kind("binary") add_files("src/*.c") ``` Of course, we can also use `add_requires("xx", {configs = {vs_runtime = "MD"}})` to modify the vs runtime library for specific packages. We can also use `xmake f --vs_runtime='MD'` to switch it globally through parameter configuration. Issues related to this api: [#1071](https://github.com/xmake-io/xmake/issues/1071#issuecomment-750817681) ## set\_group ### Set target group #### Function Prototype ::: tip API ```lua set_group(group: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | group | Group name string, such as "test", "libs", "tools" | #### Usage #### Used for group display of project files This interface can be used to generate the vs/vsxmake project. The directory tree of the internal subprojects of the vs project is grouped and displayed according to the specified structure. However, grouping support may be added to other modules in the future. For example, for the following grouping configuration: ```lua add_rules("mode.debug", "mode.release") target("test1") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test2") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test3") set_kind("binary") add_files("src/*.cpp") set_group("group1/group2") target("test4") set_kind("binary") add_files("src/*.cpp") set_group("group3/group4") target("test5") set_kind("binary") add_files("src/*.cpp") target("test6") set_kind("binary") add_files("src/*.cpp") ``` The effect of the generated VS project directory structure is as follows: ![](/assets/img/manual/set_group.png) For more details, please see: [#1026](https://github.com/xmake-io/xmake/issues/1026) #### Compile and specify a batch of target programs We can use `set_group()` to mark a given target as `test/benchmark/...` and use `set_default(false)` to disable to build it by default. Then, through the `xmake -g xxx` command, you can specify to build a batch of target programs. For example, we can use this feature to build all tests. ```lua target("test1") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") target("test2") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") ``` ```sh $ xmake -g test $ xmake --group=test ``` #### Run a specified batch of target programs We can also specify to run all test programs with the `test` group by setting the group. ```sh $ xmake run -g test $ xmake run --group=test ``` In addition, we can also support grouped pattern matching: ``` $ xmake build -g test_* $ xmake run -g test/foo_* $ xmake build -g bench* $ xmake run -g bench* ``` For more information: [#1913](https://github.com/xmake-io/xmake/issues/1913) ## add\_filegroups ### Add Source file groups #### Function Prototype ::: tip API ```lua add_filegroups(name: , files: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Group name string, such as "source", "header", "test" | | files | Source file path string or array, supports wildcard matching patterns | | ... | Variable parameters, can pass multiple source file path strings | #### Usage This interface is currently used to group the source files generated by the vs/vsxmake/cmakelists generator. If you don't set up grouping, Xmake will also display them in tree mode by default, but in some extreme cases, the directory hierarchy is not very good, e.g. ```lua target("test") set_kind("binary") add_files("... /... /... /... /src/**.cpp") ``` ![](/assets/img/manual/filegroup1.png) Two main presentation modes are currently supported. * plain: flat mode * tree: tree display, which is also the default mode Also, it supports grouping of files added by `add_headerfiles`. #### Set the group and specifies the root directory ```lua target("test") set_kind("binary") add_files("... /... /... /... /src/**.cpp") add_filegroups("group1/group2", {rootdir = "... /... /... /... /"}) ``` ![](/assets/img/manual/filegroup2.png) #### Set the group and specifies the file matching pattern ```lua target("test") set_kind("binary") add_files("... /... /... /... /src/**.cpp") add_filegroups("group1/group2", {rootdir = "... /... /... /... /", files = {"src/**.cpp"}}) ``` #### Show as flat mode In this mode, all source files ignore the nested directory hierarchy and are displayed at the same level under grouping. ```lua target("test") set_kind("binary") add_files("... /... /... /... /src/**.cpp") add_filegroups("group1/group2", {rootdir = "... /... /... /... /", mode = "plain"}) ``` ![](/assets/img/manual/filegroup3.png) ## set\_exceptions ### Enabling or disabling exceptions #### Function Prototype ::: tip API ```lua set_exceptions(exceptions: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | exceptions | Exception mode string or array, such as "cxx", "objc", "no-cxx" | | ... | Variable parameters, can pass multiple exception mode strings | #### Usage We can configure C++/Objc exceptions to be enabled and disabled via this configuration. Normally, if we configure them via the add\_cxxflags interface, it would be cumbersome for the compiler to handle them separately, depending on the platform. For example ```lua on_config(function (target) if (has_tool("cxx", "cl")) then add("cxflags", "/EHsc", {force = true}) add("defines", "_HAS_EXCEPTIONS=1", {force = true}) elseif(has_tool("cxx", "clang") or has_tool("cxx", "clang-cl")) then add("cxflags", "-fexceptions", {force = true}) add("cxflags", "-fcxx-exceptions", {force = true}) end end) ``` And with this interface, we can abstract to configure them in a compiler-independent way. Enabling C++ exceptions: ```lua set_exceptions("cxx") ``` Disable C++ exceptions: ```lua set_exceptions("no-cxx") ``` We can also configure to turn on objc exceptions at the same time. ```lua set_exceptions("cxx", "objc") ``` or disable them. ```lua set_exceptions("no-cxx", "no-objc") ``` Xmake will automatically adapt the flags internally to the different compilers. ## set\_encodings ### Set encodings #### Function Prototype ::: tip API ```lua set_encodings(encodings: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | encodings | Encoding string or array, such as "utf-8", "gb2312" | | ... | Variable parameters, can pass multiple encoding strings | #### Usage This is a new interface in version 2.8.2, we can use this interface to set the encoding of source and target files. All supported encodings: utf-8, gb2312 (msvc) By default, just specifying the encoding will work for both the source and target files. ```lua -- for all source/target encodings set_encodings("utf-8") -- msvc: /utf-8 ``` It is equivalent to: ```lua set_encodings("source:utf-8", "utf-8") ``` And it only supports utf-8 encodings for now, but will be expanded in the future. If we just want to set the source file encoding or the target file encoding separately, we can do that too. #### Set source encoding Usually this refers to the encoding of the source file of the compiled code, and we can set it like this. ```lua -- gcc/clang: -finput-charset=UTF-8, msvc: -source-charset=utf-8 set_encodings("source:utf-8") ``` #### Set the target file encoding It usually refers to the runtime output encoding of the target executable. ```lua -- gcc/clang: -fexec-charset=UTF-8, msvc: -target-charset=utf-8 set_encodings("utf-8") ``` ## add\_forceincludes ### forceincludes #### Function Prototype ::: tip API ```lua add_forceincludes(includes: , ..., { public|interface|private = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Header file path string or array, such as "config.h" | | ... | Variable parameters, can pass multiple header file path strings | | public|interface|private | Visibility setting, see [Visibility Settings](#visibility) for details | #### Usage This is a new interface in 2.8.2 for forcing `includes` headers directly into configuration files. ```lua add_forceincludes("config.h") ``` It works like `#include `, but you don't need to add it explicitly in the source code. Also, its search path is controlled by `add_includedirs` instead of the direct config file path. ```lua add_forceincludes("config.h") add_includedirs("src") ``` By default add\_forceincludes matches c/c++/objc, if you just want to match c++ you can do so: ```lua add_forceincludes("config.h", {sourcekinds = "cxx"}) ``` If you want to match multiple source file types at the same time, that's also possible: ```lua add_forceincludes("config.h", {sourcekinds = {"cxx", "mxx"}}) ``` ## add\_extrafiles ### Adding Extra Files #### Function Prototype ::: tip API ```lua add_extrafiles(extrafiles: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | extrafiles | Extra file path string or array, such as "assets/other.txt" | | ... | Variable parameters, can pass multiple extra file path strings | #### Usage This interface, also new in 2.8.2, is mainly used in projects generated by the vs/vsxmake project generator to add extra files to the project list, so that users can also quickly click on them to edit them, even though they are not code files. In the future, we may use this interface for more other things as well. ```lua add_extrafiles("assets/other.txt") ``` ## add\_tests ### Add test case #### Function Prototype ::: tip API ```lua add_tests(tests: , ..., { runargs = , runenvs =
, timeout = , ... = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tests | Test case name string or array, such as "test1", "test2" | | ... | Variable parameters, can pass multiple test case name strings | | runargs | Test run argument string or array, optional | | runenvs | Test run environment variable table, optional | | timeout | Test timeout time in seconds, optional | | ... | Other test configuration parameters, optional | #### Usage Starting from version 2.8.5, we have added a built-in test command: `xmake test`. We only need to configure some test cases through add\_tests on the target that needs to be tested to automatically execute the test. Even if the current target is set to `set_default(false)`, when executing tests, xmake will still automatically compile them first, and then automatically run all tests. We can first look at an overall example to get a rough idea of what it looks like. ```lua add_rules("mode.debug", "mode.release") for _, file in ipairs(os.files("src/test_*.cpp")) do local name = path.basename(file) target(name) set_kind("binary") set_default(false) add_files("src/" .. name .. ".cpp") add_tests("default") add_tests("args", {runargs = {"foo", "bar"}}) add_tests("pass_output", {trim_output = true, runargs = "foo", pass_outputs = "hello foo"}) add_tests("fail_output", {fail_outputs = {"hello2 .*", "hello xmake"}}) end ``` This example automatically scans the `test_*.cpp` source files in the source code directory, and then automatically creates a test target for each file. It is set to `set_default(false)`, which means that under normal circumstances, it will not be compiled by default. they. However, if you execute `xmake test` for testing, they will be automatically compiled and then tested. The running effect is as follows: ```sh ruki-2:test ruki$ xmake test running tests ... [ 2%]: test_1/args .................................... passed 7.000s [ 5%]: test_1/default .................................... passed 5.000s [ 8%]: test_1/fail_output .................................... passed 5.000s [ 11%]: test_1/pass_output .................................... passed 6.000s [ 13%]: test_2/args .................................... passed 7.000s [ 16%]: test_2/default .................................... passed 6.000s [ 19%]: test_2/fail_output .................................... passed 6.000s [ 22%]: test_2/pass_output .................................... passed 6.000s [ 25%]: test_3/args .................................... passed 7.000s [ 27%]: test_3/default .................................... passed 7.000s [ 30%]: test_3/fail_output .................................... passed 6.000s [ 33%]: test_3/pass_output .................................... passed 6.000s [ 36%]: test_4/args .................................... passed 6.000s [ 38%]: test_4/default .................................... passed 6.000s [ 41%]: test_4/fail_output .................................... passed 5.000s [ 44%]: test_4/pass_output .................................... passed 6.000s [ 47%]: test_5/args .................................... passed 5.000s [ 50%]: test_5/default .................................... passed 6.000s [ 52%]: test_5/fail_output .................................... failed 6.000s [ 55%]: test_5/pass_output .................................... failed 5.000s [ 58%]: test_6/args .................................... passed 7.000s [ 61%]: test_6/default .................................... passed 6.000s [ 63%]: test_6/fail_output .................................... passed 6.000s [ 66%]: test_6/pass_output .................................... passed 6.000s [ 69%]: test_7/args .................................... failed 6.000s [ 72%]: test_7/default .................................... failed 7.000s [ 75%]: test_7/fail_output .................................... failed 6.000s [ 77%]: test_7/pass_output .................................... failed 5.000s [ 80%]: test_8/args .................................... passed 7.000s [ 83%]: test_8/default .................................... passed 6.000s [ 86%]: test_8/fail_output .................................... passed 6.000s [ 88%]: test_8/pass_output .................................... failed 5.000s [ 91%]: test_9/args .................................... passed 6.000s [ 94%]: test_9/default .................................... passed 6.000s [ 97%]: test_9/fail_output .................................... passed 6.000s [100%]: test_9/pass_output .................................... passed 6.000s 80% tests passed, 7 tests failed out of 36, spent 0.242s ``` ![](/assets/img/manual/xmake-test1.png) We can also execute `xmake test -vD` to view detailed test failure error messages: ![](/assets/img/manual/xmake-test2.png) #### Run the specified test target We can also specify to run a test with a specified ```sh $ xmake test targetname/testname ``` Or run all tests of a target or a batch of tests by pattern matching: ```sh $ xmake test targetname/* $ xmake test targetname/foo* ``` You can also run tests with the same name for all targets: ```sh $ xmake test */testname ``` #### Parallelize running tests In fact, the default is to run in parallel, but we can adjust the parallelism of the operation through `-jN`. ```sh $ xmake test -jN ``` #### Run tests in groups ```sh $ xmake test -g "foo" $ xmake test -g "foo*" ``` #### Add test to target (no parameters) If no parameters are configured, and only the test name is configured to `add_tests`, then it is only tested whether the target program will fail to run, and whether the test passes is judged based on the exit code. ``` target("test") add_tests("testname") ``` #### Configure running parameters We can also use `{runargs = {"arg1", "arg2"}}` to configure `add_tests` to specify the parameters that the test needs to run. In addition, a target can be configured with multiple test cases at the same time, and each test case can be run independently without conflicting with each other. ```lua target("test") add_tests("testname", {runargs = "arg1"}) add_tests("testname", {runargs = {"arg1", "arg2"}}) ``` If we do not configure runargs to `add_tests`, then we will also try to get the running parameters set by `set_runargs` from the bound target. ```lua target("test") add_tests("testname") set_runargs("arg1", "arg2") ``` #### Configure running directory We can also set the current working directory of the test run through rundir, for example: ```lua targett("test") add_tests("testname", {rundir = os.projectdir()}) ``` If we do not configure rundir to `add_tests`, then we will also try to obtain the running directory set by `set_rundir` from the bound target. ```lua target("test") add_tests("testname") set_rundir("$(projectdir)") ``` #### Configure the running environment We can also set some runtime environment variables through runenvs, for example: ```lua target("test") add_tests("testname", {runenvs = {LD_LIBRARY_PATH = "/lib"}}) ``` If we do not configure runenvs to `add_tests`, then we will also try to obtain the running environment set by `add_runenvs` from the bound target. ```lua target("test") add_tests("testname") add_runenvs("LD_LIBRARY_PATH", "/lib") ``` #### Matching output results By default, `xmake test` will determine whether the test passed based on whether the exit code of the test run is 0. Of course, we can also determine whether the test passes by configuring whether the output result of the test run meets our specified matching pattern. Mainly controlled by these two parameters: | Parameters | Description | | --- | --- | | pass\_outputs | The test passes if the outputs match | | fail\_outputs | If the outputs match, the test fails | What is passed into `pass_outputs` and `fail_outputs` is a list of lua matching patterns, but the patterns are slightly simplified, such as the processing of `*`. If the match is successful, the test passes and can be configured like this: ```lua target("test") add_tests("testname1", {pass_outputs = "hello"}) add_tests("testname2", {pass_outputs = "hello *"}) add_tests("testname3", {pass_outputs = {"hello", "hello *"}}) ``` If the match is successful, the test fails. You can configure it like this: ```lua target("test") add_tests("testname1", {fail_outputs = "hello"}) add_tests("testname2", {fail_outputs = "hello *"}) add_tests("testname3", {fail_outputs = {"hello", "hello *"}}) ``` We can also configure them simultaneously: ```lua target("test") add_tests("testname", {pass_outputs = "foo", fail_outputs = "hello"}) ``` Since some test output results will have some newline or other blank characters at the end, which interferes with the matching mode, we can configure `trim_output = true` to truncate the blank characters before matching. ```lua target("test") add_tests("testname", {trim_output = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` We can also configure `{plain = true}` to disable lua pattern matching and only do the most basic flat text matching. ```lua target("test") add_tests("testname", {plain = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` #### Configure test group We can also configure a test group through `group = "foo"` for group testing: ```lua target("test") add_tests("testname1", {group = "foo"}) add_tests("testname2", {group = "foo"}) add_tests("testname3", {group = "bar"}) add_tests("testname4", {group = "bae"}) ``` Where testname1/testname2 is a group foo, and the other two are in another group. Then, we can use `xmake test -g groupname` to perform group testing. ```sh $ xmake test -g "foo" $ xmake test -g "foo*" ``` :::tip NOTE Running grouping also supports pattern matching. ::: In addition, if the `group` parameter is not set to `add_tests`, we can also get the group name bound to the target by default. ```lua target("test") add_tests("testname") set_group("foo") ``` #### Custom test script We have also added `before_test`, `on_test` and `after_test` configuration scripts. Users can customize them in the rule and target fields to implement customized test execution. ```lua target("test") on_test(function (target, opt) print(opt.name, opt.runenvs, opt.runargs, opt.pass_outputs) -- do test --... -- passed return true -- failed return false, errors end) ``` Among them, all parameters passed into `add_tests` can be obtained in opt. We customize the test logic in on\_test, and then return true to indicate that the test passed, return false to indicate that the test failed, and then continue to return the error message of test failure. #### Automated build Since the test target usually does not need to be built during the normal development build phase, we will set `set_default(false)`. ```lua target("test") add_tests("testname") set_default(false) ``` However, when running `xmake test` for testing, the targets corresponding to these tests will still be automatically built to ensure that they can be run. ```sh $ xmake test [25%]: cache compiling.release src/main.cpp [50%]: linking.release test running tests... [100%]: test/testname ............................. passed 6.000s 100% tests passed, 0 tests failed out of 1, spent 0.006s ``` #### Marking tests as expected to fail You can mark a test as *expected to fail* using the `should_fail` option. This is useful when validating: * crash handling * assertion or contract violations * features under development or known to be broken If a test marked with `should_fail = true` fails during execution, it is treated as a **success**. If it unexpectedly passes, the result will be highlighted in the test report as an **unexpected pass**, and the test will be marked as failed. The test summary will also group expected failures and unexpected passes accordingly. ```lua target("test") set_kind("binary") add_files("tests/test_foo.cpp") add_tests("crash_div_by_zero", { runargs = { "--div-zero" }, should_fail = true }) add_tests("crash_div_by_zero_fails", { runargs = { "--div-zero" } }) add_tests("normal_behavior", { runargs = { "--safe" } }) add_tests("normal_behavior_unexpected_pass", { runargs = { "--safe" }, should_fail = true }) ``` Example output: ``` ... report of tests: [ 25%]: test/crash_div_by_zero ............. expected failure 0.004s [ 50%]: test/crash_div_by_zero_fails........ failed 0.004s [ 75%]: test/normal_behavior ............... passed 0.015s [100%]: test/normal_behavior_unexpected_pass unexpected pass 0.015s Detailed summary: Failed tests: - test/crash_div_by_zero_fails Unexpected passes: - test/normal_behavior_unexpected_pass Expected failures: - test/crash_div_by_zero 50% tests passed, 1 test(s) failed, 1 unexpected pass(es), 1 expected failure(s) out of 4, spent 0.038s ``` #### Terminate if the first test fails By default, `xmake test` will wait until all tests have been run, no matter how many of them failed. Sometimes, we want to interrupt the test directly if the first test fails, then we can enable it through the following configuration: ```lua set_policy("test.stop_on_first_failure", true) ``` #### If the test fails, return zero By default, as long as a test fails, it will return a non-zero exit code when `xmake test` is completed. This is very useful for some CI environments and can interrupt other CI scripts to continue running. Then the trigger signal tells CI that we need to generate test reports and alarms. Then, if we want to suppress this behavior, we can force the exit code of `xmake test` to always be set to 0. ```lua set_policy("test.return_zero_on_failure", true) ``` #### Only test compilation Sometimes, we just want to test whether the code compiles or fails without running them. This can be achieved by configuring `build_should_pass` and `build_should_fail`. ```lua target("test_10") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_fail", {build_should_fail = true}) target("test_11") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_pass", {build_should_pass = true}) ``` This is usually used in scenarios with `static_assert` in some test code, for example: ```c++ template bool foo(T val) { if constexpr (std::is_same_v) { printf("int!\n"); } else if constexpr (std::is_same_v) { printf("float!\n"); } else { static_assert(false, "unsupported type"); } } int main(int, char**) { foo("BAD"); return 0; } ``` #### Configure additional code compilation When configuring test cases, we can also configure additional code that needs to be compiled for each test, as well as some macro definitions to implement inline testing. xmake will compile an independent executable program for each test to run it, but this will not affect the compilation results of the target in the production environment. ```lua target("test_13") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_1", {files = "tests/stub_1.cpp", defines = "STUB_1"}) target("test_14") set_kind("binary") set_default(false) add_files("src/test_2.cpp") add_tests("stub_2", {files = "tests/stub_2.cpp", defines = "STUB_2"}) target("test_15") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_n", {files = "tests/stub_n*.cpp", defines = "STUB_N"}) ``` Taking doctest as an example, we can externally unit test without modifying any main.cpp: ```lua add_rules("mode.debug", "mode.release") add_requires("doctest") target("doctest") set_kind("binary") add_files("src/*.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { files = testfile, remove_files = "src/main.cpp", languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` Defining DOCTEST\_CONFIG\_IMPLEMENT\_WITH\_MAIN will introduce additional main entry function, so we need to configure remove\_files to remove the existing main.cpp file. The running effect is as follows: ```sh ruki-2:doctest ruki$ xmake test running tests... [50%]: doctest/test_1 ........................ failed 0.009s [100%]: doctest/test_2 ........................ passed 0.009s 50% tests passed, 1 tests failed out of 2, spent 0.019s ruki-2:doctest ruki$ xmake test -v running tests... [50%]: doctest/test_1 ........................ failed 0.026s [doctest] doctest version is "2.4.11" [doctest] run with "--help" for options ================================================== ============================= tests/test_1.cpp:7: TEST CASE: testing the factorial function tests/test_1.cpp:8: ERROR: CHECK( factorial(1) == 10 ) is NOT correct! values: CHECK( 1 == 10 ) ================================================== ============================= [doctest] test cases: 1 | 0 passed | 1 failed | 0 skipped [doctest] assertions: 4 | 3 passed | 1 failed | [doctest] Status: FAILURE! run failed, exit code: 1 [100%]: doctest/test_2 ........................ passed 0.010s 50% tests passed, 1 tests failed out of 2, spent 0.038s ``` #### Test dynamic library Usually, `add_tests` is only used to run tests on executable programs. Running dynamic libraries requires an additional main entry, so we need to configure an additional executable program to load it, for example: ```lua target("doctest_shared") set_kind("shared") add_files("src/foo.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { kind = "binary", files = testfile, languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` Each unit test can be changed to a binary executable program through `kind = "binary"`, and the main entry function can be introduced through DOCTEST\_CONFIG\_IMPLEMENT\_WITH\_MAIN. This enables external runnable unit tests in dynamic library targets. #### Configure run timeout If some test programs get stuck if they run for a long time without exiting, we can force them to exit and return failure by configuring a timeout. ````lua target("test_timeout") set_kind("binary") set_default(false) add_files("src/run_timeout.cpp") add_tests("run_timeout", {run_timeout = 1000}) `` ```sh $ xmake test [100%]: test_timeout/run_timeout .................................... failed 1.006s run failed, exit code: -1, exit error: wait process timeout ```` --- --- url: /api/scripts/extension-modules/core/base/queue.md --- # queue The queue module provides a First-In-First-Out (FIFO) queue data structure that supports efficient enqueue and dequeue operations. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.queue")` ::: ## queue.new * Create an empty queue #### Function Prototype ::: tip API ```lua queue.new() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a new empty queue object. ```lua local q = queue.new() print(q:size()) -- Output: 0 print(q:empty()) -- Output: true ``` ## queue:push * Enqueue (add element to the end) #### Function Prototype ::: tip API ```lua queue:push(item: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | item | Required. Element to add to the queue | #### Usage Adds an element to the end of the queue. This is a fundamental queue operation. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:size()) -- Output: 3 print(q:first()) -- Output: 1 (front element) print(q:last()) -- Output: 3 (rear element) ``` The queue supports storing various types of values: ```lua local q = queue.new() q:push("hello") q:push(123) q:push({key = "value"}) q:push(true) ``` ## queue:pop * Dequeue (remove and return element from the front) #### Function Prototype ::: tip API ```lua queue:pop() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes and returns an element from the front of the queue. Returns nil if the queue is empty. This is a fundamental queue operation. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) local first = q:pop() print(first) -- Output: 1 print(q:first()) -- Output: 2 (new front element) print(q:size()) -- Output: 2 ``` Process the queue until empty: ```lua local q = queue.new() q:push(10) q:push(20) q:push(30) while not q:empty() do local item = q:pop() print(item) -- Output: 10, 20, 30 end ``` ## queue:first * Peek at the front element #### Function Prototype ::: tip API ```lua queue:first() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the first element (front) of the queue without removing it. Returns nil if the queue is empty. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:first()) -- Output: 1 print(q:size()) -- Output: 3 (element not removed) ``` Used to check the next element to be processed: ```lua local q = queue.new() q:push("task1") q:push("task2") -- Peek without processing if q:first() == "task1" then print("Next task is task1") end ``` ## queue:last * Peek at the rear element #### Function Prototype ::: tip API ```lua queue:last() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the last element (rear) of the queue without removing it. Returns nil if the queue is empty. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:last()) -- Output: 3 print(q:size()) -- Output: 3 (element not removed) ``` ## queue:size * Get queue size #### Function Prototype ::: tip API ```lua queue:size() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns the number of elements in the queue. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:size()) -- Output: 3 q:pop() print(q:size()) -- Output: 2 ``` ## queue:empty * Check if queue is empty #### Function Prototype ::: tip API ```lua queue:empty() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns true if the queue is empty (contains no elements). ```lua local q = queue.new() print(q:empty()) -- Output: true q:push(1) print(q:empty()) -- Output: false ``` Used for processing queue in a loop: ```lua local q = queue.new() q:push("item1") q:push("item2") q:push("item3") while not q:empty() do local item = q:pop() print("Processing:", item) end ``` ## queue:clear * Clear the queue #### Function Prototype ::: tip API ```lua queue:clear() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Removes all elements from the queue, resetting it to an empty queue. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:size()) -- Output: 3 q:clear() print(q:size()) -- Output: 0 print(q:empty()) -- Output: true ``` ## queue:clone * Clone the queue #### Function Prototype ::: tip API ```lua queue:clone() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a complete copy of the queue, with the new queue being independent of the original. Used for saving queue snapshots: ```lua local q1 = queue.new() q1:push(1) q1:push(2) q1:push(3) local q2 = q1:clone() -- Modifying the copy doesn't affect the original q2:pop() print(q1:size()) -- Output: 3 (original unchanged) print(q2:size()) -- Output: 2 (copy modified) ``` ## queue:items * Iterate forward through the queue #### Function Prototype ::: tip API ```lua queue:items() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an iterator function for traversing all elements in the queue from front to rear. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) q:push(4) q:push(5) for item in q:items() do print(item) -- Output: 1, 2, 3, 4, 5 end ``` Verify queue order: ```lua local q = queue.new() q:push(10) q:push(20) q:push(30) local idx = 1 local expected = {10, 20, 30} for item in q:items() do assert(item == expected[idx]) idx = idx + 1 end ``` ::: tip TIP Iteration does not modify the queue content; elements remain in the queue after iteration. ::: ## queue:ritems * Iterate backward through the queue #### Function Prototype ::: tip API ```lua queue:ritems() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Returns an iterator function for traversing all elements in the queue from rear to front. ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) q:push(4) q:push(5) for item in q:ritems() do print(item) -- Output: 5, 4, 3, 2, 1 end ``` ::: tip TIP The queue module implements a standard FIFO (First-In-First-Out) queue, suitable for task scheduling, message queues, breadth-first search, and other scenarios. Both enqueue (push) and dequeue (pop) operations have O(1) time complexity. ::: ::: warning WARNING * Queue is a FIFO structure; elements that are enqueued first are dequeued first * `pop()` operation removes the element, `first()` only peeks without removing * Calling `pop()` on an empty queue returns nil * Iteration (`items()`, `ritems()`) does not modify queue content ::: --- --- url: /zh/api/scripts/extension-modules/core/base/queue.md --- # queue queue 模块提供了先进先出(FIFO)队列数据结构,支持高效的入队和出队操作。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.queue")` ::: ## queue.new * 创建空的队列 #### 函数原型 ::: tip API ```lua queue.new() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建一个新的空队列对象。 ```lua local q = queue.new() print(q:size()) -- 输出: 0 print(q:empty()) -- 输出: true ``` ## queue:push * 入队(在队尾添加元素) #### 函数原型 ::: tip API ```lua queue:push(item: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | item | 要添加到队列的元素 | #### 用法说明 将元素添加到队列的尾部。这是队列的基本操作。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:size()) -- 输出: 3 print(q:first()) -- 输出: 1 (队首元素) print(q:last()) -- 输出: 3 (队尾元素) ``` 队列支持存储各种类型的值: ```lua local q = queue.new() q:push("hello") q:push(123) q:push({key = "value"}) q:push(true) ``` ## queue:pop * 出队(从队首移除并返回元素) #### 函数原型 ::: tip API ```lua queue:pop() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 从队列的头部移除并返回一个元素。如果队列为空,返回 nil。这是队列的基本操作。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) local first = q:pop() print(first) -- 输出: 1 print(q:first()) -- 输出: 2 (新的队首元素) print(q:size()) -- 输出: 2 ``` 处理队列直到为空: ```lua local q = queue.new() q:push(10) q:push(20) q:push(30) while not q:empty() do local item = q:pop() print(item) -- 输出: 10, 20, 30 end ``` ## queue:first * 查看队首元素 #### 函数原型 ::: tip API ```lua queue:first() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回队列的第一个元素(队首),不移除。如果队列为空,返回 nil。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:first()) -- 输出: 1 print(q:size()) -- 输出: 3 (元素未被移除) ``` 用于查看下一个要处理的元素: ```lua local q = queue.new() q:push("task1") q:push("task2") -- 查看但不处理 if q:first() == "task1" then print("下一个任务是 task1") end ``` ## queue:last * 查看队尾元素 #### 函数原型 ::: tip API ```lua queue:last() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回队列的最后一个元素(队尾),不移除。如果队列为空,返回 nil。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:last()) -- 输出: 3 print(q:size()) -- 输出: 3 (元素未被移除) ``` ## queue:size * 获取队列大小 #### 函数原型 ::: tip API ```lua queue:size() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回队列中元素的个数。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:size()) -- 输出: 3 q:pop() print(q:size()) -- 输出: 2 ``` ## queue:empty * 判断队列是否为空 #### 函数原型 ::: tip API ```lua queue:empty() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回 true 表示队列为空(不包含任何元素)。 ```lua local q = queue.new() print(q:empty()) -- 输出: true q:push(1) print(q:empty()) -- 输出: false ``` 用于循环处理队列: ```lua local q = queue.new() q:push("item1") q:push("item2") q:push("item3") while not q:empty() do local item = q:pop() print("处理:", item) end ``` ## queue:clear * 清空队列 #### 函数原型 ::: tip API ```lua queue:clear() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 删除队列中的所有元素,重置为空队列。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) print(q:size()) -- 输出: 3 q:clear() print(q:size()) -- 输出: 0 print(q:empty()) -- 输出: true ``` ## queue:clone * 克隆队列 #### 函数原型 ::: tip API ```lua queue:clone() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建队列的完整副本,新队列与原队列独立。 用于保存队列快照: ```lua local q1 = queue.new() q1:push(1) q1:push(2) q1:push(3) local q2 = q1:clone() -- 修改副本不影响原队列 q2:pop() print(q1:size()) -- 输出: 3 (原队列不变) print(q2:size()) -- 输出: 2 (副本已修改) ``` ## queue:items * 正向遍历队列 #### 函数原型 ::: tip API ```lua queue:items() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回一个迭代器函数,用于从队首到队尾遍历队列中的所有元素。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) q:push(4) q:push(5) for item in q:items() do print(item) -- 输出: 1, 2, 3, 4, 5 end ``` 验证队列顺序: ```lua local q = queue.new() q:push(10) q:push(20) q:push(30) local idx = 1 local expected = {10, 20, 30} for item in q:items() do assert(item == expected[idx]) idx = idx + 1 end ``` ::: tip 提示 遍历不会修改队列内容,遍历后元素仍然在队列中。 ::: ## queue:ritems * 反向遍历队列 #### 函数原型 ::: tip API ```lua queue:ritems() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回一个迭代器函数,用于从队尾到队首遍历队列中的所有元素。 ```lua local q = queue.new() q:push(1) q:push(2) q:push(3) q:push(4) q:push(5) for item in q:ritems() do print(item) -- 输出: 5, 4, 3, 2, 1 end ``` ::: tip 提示 queue 模块实现了标准的 FIFO(先进先出)队列,适用于任务调度、消息队列、广度优先搜索等场景。队列的入队(push)和出队(pop)操作都是 O(1) 时间复杂度。 ::: ::: warning 注意 * 队列是 FIFO 结构,先入队的元素先出队 * `pop()` 操作会移除元素,`first()` 只是查看不移除 * 从空队列 `pop()` 会返回 nil * 遍历(`items()`、`ritems()`)不会修改队列内容 ::: --- --- url: /guide/quick-start.md --- # Quick Start ## Installation ::: tip NOTE Xmake is not recommended for root installation, because this is very insecure. If you must install as root and Xmake fails to run, please pass the `--root` parameter as prompted or set `XMAKE_ROOT=y`. The environment variable is forcibly enabled, but please be aware of the risk of operating system file errors under root. ::: ::: code-group ```sh [curl] curl -fsSL https://xmake.io/shget.text | bash ``` ```sh [wget] wget https://xmake.io/shget.text -O - | bash ``` ```powershell [powershell] irm https://xmake.io/psget.text | iex ``` ::: If you want to install a specific version or branch, you can append the version number or branch parameter at the end ```sh curl -fsSL https://xmake.io/shget.text | bash -s dev curl -fsSL https://xmake.io/shget.text | bash -s v2.7.7 & ([ScriptBlock]::Create((irm https://xmake.io/psget.text))) -version 2.7.7 ``` ::: tip NOTE If the ps script execution prompt fails, you can try to execute it in administrator mode. ::: ### Windows 1. Download the Xmake windows installer from [Releases](https://github.com/xmake-io/xmake/releases) 2. Run xmake-\[version].\[win32|win64].exe ::: tip NOTE Releases/xmake-\[version].\[win32|win64].zip does not have an installer. We need to unzip it and add the PATH environment variable ourselves. ::: In addition, the installation package with `xmake-tinyc-xxx.exe` integrates the tinyc compiler environment and comes with libc and winapi header files. By installing this package, you can compile C programs without MSVC. This is very useful for users who want to write some C tests or algorithm code temporarily, but don't want to install MSVC. However, the installation package will be slightly larger (by 2-3MB). ::: code-group ```sh [scoop] scoop install xmake ``` ```sh [winget] winget install xmake ``` ::: ### Msys/Mingw ::: code-group ```sh [mingw64] pacman -Sy mingw-w64-x86_64-xmake ``` ```sh [mingw32] pacman -Sy mingw-w64-i686-xmake ``` ::: ### MacOS ```sh brew install xmake ``` ### Linux distributions ::: code-group ```sh [Arch Linux] sudo pacman -S xmake ``` ```sh [Alpine] sudo apk add xmake ``` ```sh [ubuntu] sudo apt install xmake ``` ```sh [debian] sudo apt install xmake ``` ```sh [fedora] sudo dnf install xmake ``` ::: ### Ubuntu PPA ```sh sudo add-apt-repository ppa:xmake-io/xmake sudo apt update sudo apt install xmake ``` ### Gentoo 1. Refer to [here](https://wiki.gentoo.org/wiki/Project:GURU/Information_for_End_Users) to add GURU to your system repository 2. Install dev-util/xmake ```sh sudo emerge -a --autounmask dev-util/xmake ``` ### Other Linux Download xmake `xmake-x.x.x.gz.run` install package from [Releases](https://github.com/xmake-io/xmake/releases) ```sh sudo chmod 777 ./xmake-x.x.x.gz.run ./xmake-x.x.x.gz.run ``` ### FreeBSD Due to package name conflicts, only xmake-io can be used as the package name. ```sh pkg install xmake-io ``` ### Termux (Android) ```sh pkg install xmake ``` ### Bundle package If you don't want to install, we also provide another Bundle packaging format, which does not require user installation, a single executable file, can be run and used after downloading, and is easy to distribute. It will build all Lua scripts into the Xmake executable file, without the need for additional installation and configuration of any environment variables. We can get them from [Releases](https://github.com/xmake-io/xmake/releases), and there are currently some Bundle packages as follows. ``` xmake-bundle-v2.9.8.arm64.exe xmake-bundle-v2.9.8.cosmocc xmake-bundle-v2.9.8.linux.x86_64 xmake-bundle-v2.9.8.macos.arm64 xmake-bundle-v2.9.8.macos.x86_64 xmake-bundle-v2.9.8.win32.exe xmake-bundle-v2.9.8.win64.exe ``` Among them, the package with the `.cosmocc` suffix provides the ability to run across platforms, but support for Windows is still relatively weak, so it is not recommended to use it on Windows. The others are single executable files for specific platforms, and users can download and use them as needed according to their own systems. ### Source compilation and installation #### Download source code ```sh git clone --recursive https://github.com/xmake-io/xmake.git cd ./xmake ``` If you think the source from GitHub is too slow, you can clone it from the mirror source on Gitee or GitLab: ```sh git clone --recursive https://gitee.com/tboox/xmake.git git clone --recursive https://gitlab.com/tboox/xmake.git ``` ::: tip NOTE Since the current Xmake source maintains dependencies via git submodule, it is necessary to add the `--recursive` parameter to pull all submodules at the same time. Please do not download the tar.gz source directly, because GitHub does not automatically package submodules. ::: If you forget to add `--recursive` when cloning, you can also execute `git submodule update --init` to pull all submodules, for example: ```sh git submodule update --init ``` #### Build and install ::: code-group ```sh [Linux] ./configure make -j4 ./scripts/get.sh __local__ __install_only__ source ~/.xmake/profile ``` ```sh [windows] cd ./core xmake ``` ::: ::: tip NOTE `./get.sh __local__` is installed to `~/.local/xmake`, and then loaded by `source ~/.xmake/profile`. So after installation, if the current terminal fails to execute Xmake, and the prompt says it is not found, manually execute `source ~/.xmake/profile`. The next time you open the terminal, you won't need to do this again. If you encounter problems with readline, please install the readline-devel or libreadline-dev dependencies. This is optional and only needed when the `xmake lua` command executes REPL. ::: ### Update and Upgrade Starting with v2.2.3, the `xmake update` command was added to quickly update and upgrade itself. By default, it upgrades to the latest version. Of course, you can also specify a version to upgrade or roll back to: ```sh xmake update 2.7.1 ``` You can also specify an update to the master/dev branch version: ```sh xmake update master xmake update dev ``` Update from a specified git source ```sh xmake update github:xmake-io/xmake#master xmake update gitee:tboox/xmake#dev # gitee mirror ``` If you just want to update the xmake lua script changes, you can add `-s/--scriptonly` to quickly update the lua script. ```sh xmake update -s dev ``` Finally, if you want to uninstall Xmake, we're sorry to see you go! Still, it is supported: `xmake update --uninstall`. ## Create Project ```sh $ xmake create hello ``` And xmake will generate some files for a C++ language project: ``` hello ├── src │   └─main.cpp └── xmake.lua ``` It is a simple console program that only prints `hello xmake!` The content of `xmake.lua` is very simple: ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("hello") set_kind("binary") add_files("src/*.cpp") ``` ## Build Project ```sh $ cd hello $ xmake ``` ## Run Program ```sh $ xmake run ``` ## Debug Program To debug the hello program, you need to change to debug mode and build it. ```sh $ xmake config -m debug $ xmake ``` Then run the following command to debug the target program. ```sh $ xmake run -d hello ``` It will start the debugger (e.g. lldb, gdb, windbg, vsjitdebugger, ollydbg, etc.) to load your program. ```sh [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]$ ``` To study more debug commands, please see the [GDB to LLDB command map](https://lldb.llvm.org/use/map.html) If you want to use a specific debugger, try ```sh $ xmake f --debugger=gdb $ xmake run -d hello ``` ## What's Next? Continue with the guide: [Create Project](/guide/basic-commands/create-project) Check out the examples: [Examples](/examples/cpp/basic) Check out the API reference: [API Reference](/api/description/specification) --- --- url: /api/scripts/builtin-modules/raise.md --- # raise * Throwing an abort exception #### Function Prototype ::: tip API ```lua raise(message: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | message | Error message string | #### Usage 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](/api/scripts/builtin-modules/try-catch-finally), xmake will be executed. An error message is displayed. Additionally, this will cause the xmake program to terminate and exit. ```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](/api/scripts/builtin-modules/try-catch-finally) --- --- url: /zh/api/scripts/builtin-modules/raise.md --- # raise * 抛出异常 #### 函数原型 ::: tip API ```lua raise(message: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | message | 错误信息字符串 | #### 用法说明 如果想在自定义脚本、插件任务中中断xmake运行,可以使用这个接口抛出异常,如果上层没有显示调用[try-catch](/zh/api/scripts/builtin-modules/try-catch-finally)捕获的话,xmake就会中断执行,并且显示出错信息。 另外,这会导致 xmake 程序终止退出。 ```lua if (errors) raise(errors) ``` 如果在 try 块中抛出异常,就会在 catch 和 finally 中进行错误信息捕获,具体见:[try-catch](/zh/api/scripts/builtin-modules/try-catch-finally) --- --- url: /guide/extras/remote-compilation.md --- # Remote Compilation Version 2.6.5 provides remote compilation support, through which we can compile code on a remote server, run, and debug remotely. The server can be deployed on Linux/MacOS/Windows to achieve cross-platform compilation, for example: compile and run Windows programs on Linux, and compile and run macOS/Linux programs on Windows. Compared with ssh remote login and compilation, it is more stable and smoother to use. It will not cause ssh terminal input to be stuck due to network instability, and it can also quickly edit code files locally. We can even seamlessly implement remote compilation in editors and IDEs such as VS/Sublime/VSCode/IDEA without relying on the IDE's own support for remote compilation. ## Start the service ```sh $ xmake service : listening 0.0.0.0:9091 .. ``` We can also start the service and display detailed log information. ```sh $ xmake service -vD : listening 0.0.0.0:9091 .. ``` ## Start the service in Daemon mode To start and control the service when in daemon mode, you can issue the following commands: ```sh $ xmake service --start $ xmake service --restart $ xmake service --stop ``` ## Configure the server We first, run the `xmake service` command, it will automatically generate a default `server.conf` configuration file, stored in `~/.xmake/service/server.conf`. ::: tip NOTE Version 2.6.5, the configuration address is in `~/.xmake/service.conf`. Subsequent versions have made a lot of improvements and separated the configuration file. If you are using version 2.6.6 or above, please use the new configuration file. ::: Then, we edit it, fixing the server's listening port (optional). ```sh { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f" } } ``` ## Configure the client The client configuration file is in `~/.xmake/service/client.conf`, where we can configure the server address that the client needs to connect to. ::: tip NOTE Version 2.6.5, the configuration address is in `~/.xmake/service.conf`. Subsequent versions have made a lot of improvements and separated the configuration file. If you are using version 2.6.6 or above, please use the new configuration file. ::: ```sh { remote_build = { connect = "127.0.0.1:9691", token = "e438d816c95958667747c318f1532c0f" } } ``` ## User authorization ::: tip NOTE Version 2.6.6 and above supports user authentication; version 2.6.5 can only connect anonymously. ::: Before the actual connection, we briefly introduce several authentication mechanisms currently provided by the services provided by Xmake. 1. Token authentication 2. Password authentication 3. Trusted host verification ### Token Authentication This is also the default recommended method, which is more secure, more convenient to configure and connect, and does not need to enter a password every time you connect. When we execute the `xmake service` command, a server and client configuration file will be generated by default, and a default token will be automatically generated, so the local direct connection does not require any configuration. #### Server authentication configuration The server can configure multiple tokens for authorizing connections to different user hosts, and of course, can share one token. ```sh { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f" } } ``` #### Client Authentication Configuration The client only needs to add the token on the server to the corresponding client configuration. ```sh { remote_build = { connect = "127.0.0.1:9691", token = "e438d816c95958667747c318f1532c0f" } } ``` #### Manually generate new token We can also execute the following command to manually generate a new token and add it to the server configuration ourselves. ```sh $ xmake service --gen-token New token a7b9fc2d3bfca1472aabc38bb5f5d612 is generated! ``` ### Password authentication We also provide an authorization mode of password authentication. Compared with token authentication, it requires users to enter a password every time they connect, and can only be connected after the verification is passed. #### Server authentication configuration For password authentication, we do not need to manually configure the token, just execute the following command to add a user. During the adding process, the user will be prompted to enter a password. ```sh $ xmake service --add-user=ruki Please input user ruki password: 123456 Add user ruki ok! ``` Then, Xmake will generate a new token from the username and password and add it to the token list of the server configuration. ```sh { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f", "7889e25402413e93fd37395a636bf942" } } ``` Of course, we can also delete the specified user and password. ```sh $ xmake service --rm-user=ruki Please input user ruki password: 123456 Remove user ruki ok! ``` #### Client Authentication Configuration For the client, we no longer need to set the token of the server. We only need to add the user name that needs to be connected in the connection configuration to enable password authentication. The format is: `user@address:port` ```sh { remote_build = { connect = "root@127.0.0.1:9691" } } ``` ::: tip NOTE If the username is removed and the token is not configured, it is anonymous mode. If the server is not configured with a token, the authentication is completely disabled and the connection is made directly. ::: ### Trusted host verification In addition, in order to further improve security, we also provide server-side trusted host verification. If the server-configured `known_hosts` list is configured with the IP address of the client host that can be connected, then only these hosts can successfully connect to this server, and other hosts' connections to it will be prompted to be untrusted and refuse the connection, even if token and password authentication are OK. ```sh { logfile = "/Users/ruki/.xmake/service/logs.txt", server = { tokens = { "4b928c7563a0cba10ff4c3f5ca0c8e24" }, known_hosts = { "127.0.0.1", "xx.xx.xx.xx"} } } ``` ## Connect to a remote server Next, we only need to enter the root directory of the project that needs to be compiled remotely, and execute the `xmake service --connect` command to connect. If it is the token authentication mode, then no additional password input is required, and the connection is directly connected. ```sh $ xmake create test $ cd test $ xmake service --connect : connect 192.168.56.110:9091 .. : connected! : sync files in 192.168.56.110:9091 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` If it is password authentication, the user will be prompted to enter the password to continue the connection. ```sh $ xmake service --connect Please input user root password: 000000 : connect 127.0.0.1:9691 .. : connected! : sync files in 127.0.0.1:9691 .. Scanning files .. Comparing 3 files .. [+]: xmake.lua [+]: .gitignore [+]: src/main.cpp 3 files has been changed! Archiving files .. Uploading files with 1591 bytes .. : sync files ok! ``` If the password is incorrect, an error message will be displayed. ```sh $ xmake service --connect Please input user root password: 123 : connect 127.0.0.1:9691 .. : connect 127.0.0.1:9691 failed, user and password are incorrect! ``` ## Remote build project After the connection is successful, we can compile remotely like normal local compilation. ```sh $ xmake : run xmake in 192.168.56.110:9091 .. checking for platform... macosx checking for architecture ... x86_64 checking for Xcode directory ... /Applications/Xcode.app checking for Codesign Identity of Xcode ... Apple Development: waruqi@gmail.com (T3NA4MRVPU) checking for SDK version of Xcode for macosx (x86_64) ... 11.3 checking for Minimal target version of Xcode for macosx (x86_64) ... 11.4 [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! : run command ok! ``` ## Run the target program remotely We can also run and debug the compiled target program remotely like running and debugging locally. ```sh $ xmake run : run xmake run in 192.168.56.110:9091 .. hello world! : run command ok! ``` ## Remote Rebuild Project ```sh $ xmake -rv : run xmake -rv in 192.168.56.110:9091 .. [ 25%]: cache compiling.release src/main.cpp /usr/local/bin/ccache /usr/bin/xcrun -sdk macosx clang -c -Qunused-arguments -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/ MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src /main.cpp [ 50%]: linking.release test "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/test build/.objs/test/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version -min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -stdlib=libc++ -Wl,-x -lz [100%]: build ok! : run command ok! ``` ## Remote configuration compilation parameters ```sh $ xmake f --xxx --yy ``` ## Manually sync project files When connecting, the code will be automatically synchronized once, and the code will be changed later. You can execute this command to manually synchronize the changed files. ```sh $ xmake service --sync : sync files in 192.168.56.110:9091 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` ## Pull remote files In version v2.7.1, we added a parameter to pull the remote specified file. Usually, we can use it to pull the target file after the build and download the compiled library file to the local. For example: ```sh xmake service --pull 'build/**' outputdir ``` We can specify the remote path `build/**` to pull all matching files to the local outputdir directory. ## Disconnect from remote For the current project, disconnect the connection, which only affects the current project, and other projects can still be connected and compiled at the same time. ```sh $ xmake service --disconnect : disconnect 192.168.56.110:9091 .. : disconnected! ``` ## View server log ```sh $ xmake service --logs ``` ## Clean remote service cache and build files We can also manually clean any caches and build generated files from the remote. ```sh $ cd projectdir $ xmake service --clean ``` --- --- url: /guide/package-management/repository-management.md --- # Repository Management ::: warning Important Note **`xmake repo` is only used for local package repository management within the current project**, scoped to the current project. If you need to **manage repositories globally** (add, remove, view repositories), you should use the **`xrepo` CLI** command, for example: * `xrepo add-repo myrepo https://github.com/mygroup/myrepo` - Add repository globally * `xrepo rm-repo myrepo` - Remove repository globally * `xrepo list-repo` - View all global repositories For detailed documentation, see: [Getting Started with Xrepo Commands](/guide/package-management/xrepo-cli) ::: We can use `xmake repo` to manage repositories for the **current project**, and we also provide a more convenient, independent `xrepo` package manager command to install, uninstall, find, and manage packages **globally**. ```sh $ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git ``` We can also remove a repository that has already been added: ```sh $ xmake repo --remove myrepo ``` Or view all the added repositories: ```sh $ xmake repo --list ``` If the remote repository has updates, you can manually perform a repository update to get more and the latest packages: ```sh $ xmake repo -u ``` --- --- url: /guide/basic-commands/run-targets.md --- # Run targets {#run-targets} Xmake provides a built-in `xmake run` command, which can quickly run the built target program without manually finding and running the corresponding executable file. This provides a lot of convenience, and it will also automatically bind the environment required for running to ensure that resources, dynamic libraries, etc. can be automatically loaded. ## Command format ```sh $ xmake run [options] [target] [runargs] ``` :::tip NOTE The options in front are the parameter options of `xmake run`, and the `runargs` after are the executable parameters specific to the target, which can be passed into the target program as is. ::: ## Run a program Usually, we only need to execute `xmake run` to run all executable programs. ```sh $ xmake run hello world! ``` ## Run a specific target program If you want to run a specific target program, you can execute: ```sh $ xmake run foo ``` ## Run all programs If a target is configured as `default = false`, it will not be run by default. ```lua target("test") set_default(false) add_files("src/*.c") ``` If you want to run all targets, including those with `default = false`, you can pass the `-a/--all` parameter. ```sh $ xmake run -a ``` ## Pass run parameters We can also pass run parameters to internal target programs. ```sh $ xmake run foo --arg1=xxx --arg2=yyy ``` :::tip NOTE At this time, we cannot omit the target name, and must specify the target name to be run, otherwise it will cause parameter ambiguity. ::: We can also use the [set\_runargs](/api/description/project-target#set-runargs) configuration interface of the target to specify the incoming run parameters without having to specify them in the command line every time. ## Set the working directory for running By default, the working directory of `xmake run` is the directory where the executable file is located, which helps it to easily find some resources and dynamic libraries, and can also do some path isolation to avoid some programs generating files in the current project root directory. If we want to change the working directory, we can specify it through the `-w workdir` parameter. ```sh $ xmake run -w /tmp foo ``` We changed the running directory of the foo program to /tmp/. In addition, we can also specify the running parameters passed in through the target's [set\_rundir](/api/description/project-target#set-rundir) configuration interface, without having to specify them in the command line each time. ## Debugging programs We can also pass the `-d` parameter to let `xmake run` load the debugger available in the current system environment while running the program, such as: gdb/lldb. But the premise is that the current program must be compiled in debug mode, otherwise it will be difficult to debug because of the lack of necessary symbol information, and the call stack, line number, and other information will not be visible. ```sh $ xmake f -m debug $ xmake ``` ```sh $ xmake run -d hello ``` Xmake will use the debugger to load the program and run it. Currently, it supports various debuggers such as lldb, gdb, windbg, vsjitdebugger, ollydbg, etc. ```sh [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]$ ``` In addition, we can also switch to a specific debugger: ```sh $ xmake f --debugger=gdb $ xmake run -d hello ``` --- --- url: /api/scripts/extension-modules/core/base/scheduler.md --- # scheduler The scheduler module provides coroutine scheduling functionality for managing coroutine creation, execution, synchronization, and communication. This is an extension module of xmake. ::: tip NOTE To use this module, you need to import it first: `import("core.base.scheduler")` ::: ## scheduler.co\_start * Start a new coroutine task #### Function Prototype ::: tip API ```lua scheduler.co_start(cotask: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | cotask | Required. The coroutine task function to execute | | ... | Optional. Arguments to pass to the task function | #### Return Value | Type | Description | |------|-------------| | coroutine | Returns coroutine object | #### Usage Start a new coroutine and execute the specified task function. The coroutine will start executing immediately unless the scheduler is not yet started. ```lua -- Start coroutine with arguments local co = scheduler.co_start(function(name, id) print("Task", name, "with id", id, "started") end, "worker", 123) -- Coroutine group example local count = 0 scheduler.co_group_begin("test", function() for i = 1, 100 do scheduler.co_start(function() count = count + 1 end) end end) scheduler.co_group_wait("test") print("Completed", count, "tasks") ``` ## scheduler.co\_start\_named * Start a named coroutine task #### Function Prototype ::: tip API ```lua scheduler.co_start_named(coname: , cotask: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | coname | Required. Coroutine name | | cotask | Required. The coroutine task function to execute | | ... | Optional. Arguments to pass to the task function | #### Usage Start a coroutine task with a specified name for easier debugging and monitoring. ```lua -- Start multiple named coroutines for i = 1, 5 do scheduler.co_start_named("worker-" .. i, function() print("Worker", i, "started") scheduler.co_sleep(1000) print("Worker", i, "finished") end) end ``` ## scheduler.co\_start\_withopt * Start a coroutine task with options #### Function Prototype ::: tip API ```lua scheduler.co_start_withopt(opt:
, cotask: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Required. Coroutine options table | | cotask | Required. The coroutine task function to execute | | ... | Optional. Arguments to pass to the task function | #### Usage Start a coroutine task with specific options. `opt` options: * `name` - Coroutine name * `isolate` - Whether to isolate coroutine environment (default false) ```lua -- Start coroutine with isolated environment local co = scheduler.co_start_withopt({ name = "isolated-task", isolate = true }, function() -- This coroutine has independent environment variables os.setenv("CUSTOM_VAR", "isolated_value") print("Isolated task running") end) ``` ## scheduler.co\_suspend * Suspend the current coroutine #### Function Prototype ::: tip API ```lua scheduler.co_suspend() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | any | Returns arguments passed to `co_resume` | #### Usage Suspend the currently executing coroutine and yield execution to other coroutines. ```lua local co = scheduler.co_start(function() print("Step 1") scheduler.co_suspend() print("Step 2") end) -- Coroutine will suspend at co_suspend() -- Can be resumed with co_resume ``` ## scheduler.co\_resume * Resume a suspended coroutine #### Function Prototype ::: tip API ```lua scheduler.co_resume(co: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | co | Required. The coroutine object to resume | | ... | Optional. Arguments to pass to the coroutine | #### Usage Resume the specified suspended coroutine and optionally pass arguments to it. ```lua -- Coroutine communication example local co = scheduler.co_start(function() local data = scheduler.co_suspend() print("Received data:", data) local result = "processed: " .. data scheduler.co_suspend(result) end) -- Send data and get result scheduler.co_resume(co, "input data") local result = scheduler.co_resume(co) print("Got result:", result) ``` ## scheduler.co\_yield * Yield execution of the current coroutine #### Function Prototype ::: tip API ```lua scheduler.co_yield() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Yield execution of the current coroutine to allow other coroutines to run. This is a key function for cooperative multitasking. ```lua -- Cooperative task example scheduler.co_group_begin("cooperative", function() for i = 1, 3 do scheduler.co_start(function(id) for j = 1, 3 do print("Task", id, "step", j) scheduler.co_yield() end end, i) end end) scheduler.co_group_wait("cooperative") ``` ## scheduler.co\_sleep * Sleep the coroutine for specified time #### Function Prototype ::: tip API ```lua scheduler.co_sleep(ms: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | ms | Required. Sleep time in milliseconds, 0 means no sleep | #### Usage Make the current coroutine sleep for the specified number of milliseconds, during which other coroutines can continue executing. ```lua -- Timed task example for i = 1, 5 do scheduler.co_start(function(id) print("Task", id, "starting") scheduler.co_sleep(id * 500) -- Incremental delay print("Task", id, "finished") end, i) end ``` ## scheduler.co\_lock * Lock the specified lock #### Function Prototype ::: tip API ```lua scheduler.co_lock(lockname: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | lockname | Required. The name of the lock | #### Usage Acquire the lock with the specified name. If the lock is already held by another coroutine, the current coroutine will wait until the lock becomes available. ```lua -- Mutex lock example local shared_counter = 0 for i = 1, 10 do scheduler.co_start(function(id) scheduler.co_lock("counter") local old_value = shared_counter scheduler.co_sleep(100) -- Simulate work shared_counter = old_value + 1 print("Task", id, "incremented counter to", shared_counter) scheduler.co_unlock("counter") end, i) end ``` ## scheduler.co\_unlock * Release the specified lock #### Function Prototype ::: tip API ```lua scheduler.co_unlock(lockname: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | lockname | Required. The name of the lock to release | #### Usage Release the lock with the specified name, allowing other waiting coroutines to acquire the lock. ## scheduler.co\_group\_begin * Begin a coroutine group #### Function Prototype ::: tip API ```lua scheduler.co_group_begin(name: , scopefunc: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. The coroutine group name | | scopefunc | Required. The scope function | #### Usage Begin a new coroutine group. All coroutines started within the specified function will join this group. ```lua -- Batch job processing example scheduler.co_group_begin("batch_jobs", function() local jobs = {"job1", "job2", "job3", "job4", "job5"} for i, job in ipairs(jobs) do scheduler.co_start(function(job_name) print("Processing", job_name) scheduler.co_sleep(math.random(100, 500)) print("Completed", job_name) end, job) end end) ``` ## scheduler.co\_group\_wait * Wait for coroutine group completion #### Function Prototype ::: tip API ```lua scheduler.co_group_wait(name: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. The coroutine group name | | opt | Optional. Wait options | `opt` options: * `limit` - Maximum number of coroutines to wait for completion (default wait for all coroutines) #### Usage Wait for all coroutines in the specified group to complete execution. ```lua -- Limited wait example scheduler.co_group_begin("limited", function() for i = 1, 10 do scheduler.co_start(function(id) print("Task", id, "running") scheduler.co_sleep(id * 100) print("Task", id, "finished") end, i) end end) -- Only wait for first 3 coroutines to complete scheduler.co_group_wait("limited", {limit = 3}) print("First 3 tasks completed") ``` ## scheduler.co\_running * Get the currently running coroutine #### Function Prototype ::: tip API ```lua scheduler.co_running() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | coroutine | Current coroutine object (nil if no coroutine is running) | #### Usage Get the currently running coroutine object. ```lua -- Coroutine info example scheduler.co_start_named("info_task", function() local co = scheduler.co_running() print("Coroutine name:", co:name()) print("Coroutine status:", co:status()) print("Is dead:", co:is_dead()) print("Is running:", co:is_running()) print("Is suspended:", co:is_suspended()) end) ``` ## scheduler.co\_count * Get the total number of coroutines #### Function Prototype ::: tip API ```lua scheduler.co_count() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | number | Number of coroutines | #### Usage Get the total number of active coroutines in the current scheduler. ```lua -- Coroutine counting example print("Initial count:", scheduler.co_count()) for i = 1, 5 do scheduler.co_start(function() print("Task", i, "started, count:", scheduler.co_count()) scheduler.co_sleep(1000) print("Task", i, "finished, count:", scheduler.co_count()) end) end print("After starting tasks:", scheduler.co_count()) ``` ## scheduler.co\_semaphore * Create a coroutine semaphore #### Function Prototype ::: tip API ```lua scheduler.co_semaphore(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Required. Semaphore name | | value | Optional. Initial semaphore value (default 0) | #### Return Value | Type | Description | |------|-------------| | semaphore | Semaphore object | #### Usage Create a new coroutine semaphore for synchronization and resource control between coroutines. ```lua -- Semaphore example local semaphore = scheduler.co_semaphore("resource", 3) -- Allow up to 3 coroutines simultaneously for i = 1, 10 do scheduler.co_start(function(id) print("Task", id, "waiting for resource") local value = semaphore:wait(-1) -- Wait indefinitely print("Task", id, "got resource, value:", value) scheduler.co_sleep(1000) -- Simulate work semaphore:post(1) -- Release resource print("Task", id, "released resource") end, i) end ``` ## co\_semaphore:wait * Wait for semaphore #### Function Prototype ::: tip API ```lua co_semaphore:wait(timeout: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | timeout | Optional. Timeout in milliseconds, -1 means wait indefinitely, 0 means don't wait | #### Return Value | Type | Description | |------|-------------| | number | Returns semaphore value | #### Usage Wait for the semaphore. If the semaphore value is greater than 0, return immediately; otherwise, suspend the current coroutine until the semaphore becomes available. ```lua -- Semaphore wait example local semaphore = scheduler.co_semaphore("worker", 0) -- Producer coroutine scheduler.co_start(function() for i = 1, 5 do scheduler.co_sleep(1000) semaphore:post(1) print("Posted signal", i) end end) -- Consumer coroutine scheduler.co_start(function() for i = 1, 5 do local value = semaphore:wait(-1) print("Got signal", i, "value:", value) end end) ``` ## co\_semaphore:post * Release semaphore #### Function Prototype ::: tip API ```lua co_semaphore:post(value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | value | Required. The value to increase by | #### Return Value | Type | Description | |------|-------------| | number | The new semaphore value after release | #### Usage Release the semaphore by increasing its value and wake up waiting coroutines. ```lua -- Semaphore post example local semaphore = scheduler.co_semaphore("batch", 0) -- Batch processing example scheduler.co_start(function() scheduler.co_sleep(2000) print("Batch processing started") semaphore:post(5) -- Release 5 signals at once end) for i = 1, 5 do scheduler.co_start(function(id) local value = semaphore:wait(-1) print("Worker", id, "got batch signal, value:", value) end, i) end ``` ## co\_semaphore:name * Get semaphore name #### Function Prototype ::: tip API ```lua co_semaphore:name() ``` ::: #### Parameter Description No parameters required for this function. #### Return Value | Type | Description | |------|-------------| | string | Semaphore name string | #### Usage Get the semaphore name. --- --- url: /zh/api/scripts/extension-modules/core/base/scheduler.md --- # scheduler scheduler 模块提供了协程调度功能,用于管理协程的创建、执行、同步和通信。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.scheduler")` ::: ## scheduler.co\_start * 启动新的协程任务 #### 函数原型 ::: tip API ```lua scheduler.co_start(cotask: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cotask | 必需。要执行的协程任务函数 | | ... | 可选。传递给任务函数的参数 | #### 返回值说明 | 类型 | 描述 | |------|------| | coroutine | 成功时返回协程对象 | #### 用法说明 启动一个新的协程并执行指定的任务函数。协程会立即开始执行,除非调度器尚未启动。 ```lua -- 启动带参数的协程 local co = scheduler.co_start(function(name, id) print("Task", name, "with id", id, "started") end, "worker", 123) -- 协程组示例 local count = 0 scheduler.co_group_begin("test", function() for i = 1, 100 do scheduler.co_start(function() count = count + 1 end) end end) scheduler.co_group_wait("test") print("Completed", count, "tasks") ``` ## scheduler.co\_start\_named * 启动指定名称的协程任务 #### 函数原型 ::: tip API ```lua scheduler.co_start_named(coname: , cotask: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | coname | 必需。协程名称 | | cotask | 必需。要执行的协程任务函数 | | ... | 可选。传递给任务函数的参数 | #### 用法说明 启动一个带有指定名称的协程任务,便于调试和监控。 ```lua -- 启动多个命名协程 for i = 1, 5 do scheduler.co_start_named("worker-" .. i, function() print("Worker", i, "started") scheduler.co_sleep(1000) print("Worker", i, "finished") end) end ``` ## scheduler.co\_start\_withopt * 使用选项启动协程任务 #### 函数原型 ::: tip API ```lua scheduler.co_start_withopt(opt:
, cotask: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 必需。协程选项表 | | cotask | 必需。要执行的协程任务函数 | | ... | 可选。传递给任务函数的参数 | #### 用法说明 启动一个带有特定选项的协程任务。 `opt` 选项: * `name` - 协程名称 * `isolate` - 是否隔离协程环境(默认 false) ```lua -- 启动隔离环境的协程 local co = scheduler.co_start_withopt({ name = "isolated-task", isolate = true }, function() -- 这个协程有独立的环境变量 os.setenv("CUSTOM_VAR", "isolated_value") print("Isolated task running") end) ``` ## scheduler.co\_suspend * 挂起当前协程 #### 函数原型 ::: tip API ```lua scheduler.co_suspend() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | any | 返回传递给 `co_resume` 的参数 | #### 用法说明 挂起当前正在执行的协程,让出执行权给其他协程。 ```lua local co = scheduler.co_start(function() print("Step 1") scheduler.co_suspend() print("Step 2") end) -- 协程会在 co_suspend() 处挂起 -- 可以通过 co_resume 恢复执行 ``` ## scheduler.co\_resume * 恢复挂起的协程 #### 函数原型 ::: tip API ```lua scheduler.co_resume(co: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | co | 必需。要恢复的协程对象 | | ... | 可选。传递给协程的参数 | #### 用法说明 恢复指定的挂起协程,可以传递参数给协程。 ```lua -- 协程间通信示例 local co = scheduler.co_start(function() local data = scheduler.co_suspend() print("Received data:", data) local result = "processed: " .. data scheduler.co_suspend(result) end) -- 发送数据并获取结果 scheduler.co_resume(co, "input data") local result = scheduler.co_resume(co) print("Got result:", result) ``` ## scheduler.co\_yield * 让出当前协程的执行权 #### 函数原型 ::: tip API ```lua scheduler.co_yield() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 让出当前协程的执行权,允许其他协程运行。这是一个协作式多任务的关键函数。 ```lua -- 协作式任务示例 scheduler.co_group_begin("cooperative", function() for i = 1, 3 do scheduler.co_start(function(id) for j = 1, 3 do print("Task", id, "step", j) scheduler.co_yield() end end, i) end end) scheduler.co_group_wait("cooperative") ``` ## scheduler.co\_sleep * 协程睡眠指定时间 #### 函数原型 ::: tip API ```lua scheduler.co_sleep(ms: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ms | 必需。睡眠时间(毫秒),0 表示不睡眠 | #### 用法说明 让当前协程睡眠指定的毫秒数,期间其他协程可以继续执行。 ```lua -- 定时任务示例 for i = 1, 5 do scheduler.co_start(function(id) print("Task", id, "starting") scheduler.co_sleep(id * 500) -- 递增延迟 print("Task", id, "finished") end, i) end ``` ## scheduler.co\_lock * 锁定指定的锁 #### 函数原型 ::: tip API ```lua scheduler.co_lock(lockname: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | lockname | 必需。锁的名称 | #### 用法说明 获取指定名称的锁,如果锁已被其他协程持有,当前协程会等待直到锁可用。 ```lua -- 互斥锁示例 local shared_counter = 0 for i = 1, 10 do scheduler.co_start(function(id) scheduler.co_lock("counter") local old_value = shared_counter scheduler.co_sleep(100) -- 模拟工作 shared_counter = old_value + 1 print("Task", id, "incremented counter to", shared_counter) scheduler.co_unlock("counter") end, i) end ``` ## scheduler.co\_unlock * 释放指定的锁 #### 函数原型 ::: tip API ```lua scheduler.co_unlock(lockname: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | lockname | 必需。要释放的锁名称 | #### 用法说明 释放指定名称的锁,允许其他等待的协程获取锁。 ## scheduler.co\_group\_begin * 开始协程组 #### 函数原型 ::: tip API ```lua scheduler.co_group_begin(name: , scopefunc: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。协程组名称 | | scopefunc | 必需。作用域函数 | #### 用法说明 开始一个新的协程组,在指定函数内启动的所有协程都会加入该组。 ```lua -- 批量任务处理示例 scheduler.co_group_begin("batch_jobs", function() local jobs = {"job1", "job2", "job3", "job4", "job5"} for i, job in ipairs(jobs) do scheduler.co_start(function(job_name) print("Processing", job_name) scheduler.co_sleep(math.random(100, 500)) print("Completed", job_name) end, job) end end) ``` ## scheduler.co\_group\_wait * 等待协程组完成 #### 函数原型 ::: tip API ```lua scheduler.co_group_wait(name: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。协程组名称 | | opt | 可选。等待选项 | #### 用法说明 等待指定协程组中的所有协程完成执行。 `opt` 选项: * `limit` - 等待完成的最大协程数量(默认等待所有协程) ```lua -- 限制等待数量的示例 scheduler.co_group_begin("limited", function() for i = 1, 10 do scheduler.co_start(function(id) print("Task", id, "running") scheduler.co_sleep(id * 100) print("Task", id, "finished") end, i) end end) -- 只等待前 3 个协程完成 scheduler.co_group_wait("limited", {limit = 3}) print("First 3 tasks completed") ``` ## scheduler.co\_running * 获取当前运行的协程 #### 函数原型 ::: tip API ```lua scheduler.co_running() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | coroutine | 当前协程对象(如果没有运行中的协程则返回 nil) | #### 用法说明 获取当前正在运行的协程对象。 ```lua -- 协程信息获取示例 scheduler.co_start_named("info_task", function() local co = scheduler.co_running() print("Coroutine name:", co:name()) print("Coroutine status:", co:status()) print("Is dead:", co:is_dead()) print("Is running:", co:is_running()) print("Is suspended:", co:is_suspended()) end) ``` ## scheduler.co\_count * 获取协程总数 #### 函数原型 ::: tip API ```lua scheduler.co_count() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 协程数量 | #### 用法说明 获取当前调度器中活跃协程的总数。 ```lua -- 协程计数示例 print("Initial count:", scheduler.co_count()) for i = 1, 5 do scheduler.co_start(function() print("Task", i, "started, count:", scheduler.co_count()) scheduler.co_sleep(1000) print("Task", i, "finished, count:", scheduler.co_count()) end) end print("After starting tasks:", scheduler.co_count()) ``` ## scheduler.co\_semaphore * 创建协程信号量 #### 函数原型 ::: tip API ```lua scheduler.co_semaphore(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 必需。信号量名称 | | value | 可选。信号量初始值(默认为 0) | #### 返回值说明 | 类型 | 描述 | |------|------| | semaphore | 信号量对象 | #### 用法说明 创建一个新的协程信号量,用于协程间的同步和资源控制。 ```lua -- 信号量示例 local semaphore = scheduler.co_semaphore("resource", 3) -- 最多允许 3 个协程同时访问 for i = 1, 10 do scheduler.co_start(function(id) print("Task", id, "waiting for resource") local value = semaphore:wait(-1) -- 无限等待 print("Task", id, "got resource, value:", value) scheduler.co_sleep(1000) -- 模拟工作 semaphore:post(1) -- 释放资源 print("Task", id, "released resource") end, i) end ``` ## co\_semaphore:wait * 等待信号量 #### 函数原型 ::: tip API ```lua co_semaphore:wait(timeout: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | timeout | 可选。超时时间(毫秒),-1 表示无限等待,0 表示不等待 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回信号量值 | #### 用法说明 等待信号量,如果信号量值大于 0 则立即返回,否则挂起当前协程直到信号量可用。 ```lua -- 信号量等待示例 local semaphore = scheduler.co_semaphore("worker", 0) -- 生产者协程 scheduler.co_start(function() for i = 1, 5 do scheduler.co_sleep(1000) semaphore:post(1) print("Posted signal", i) end end) -- 消费者协程 scheduler.co_start(function() for i = 1, 5 do local value = semaphore:wait(-1) print("Got signal", i, "value:", value) end end) ``` ## co\_semaphore:post * 释放信号量 #### 函数原型 ::: tip API ```lua co_semaphore:post(value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | value | 必需。要增加的值 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 释放后的信号量新值 | #### 用法说明 释放信号量,增加信号量的值并唤醒等待的协程。 ```lua -- 信号量发布示例 local semaphore = scheduler.co_semaphore("batch", 0) -- 批量处理示例 scheduler.co_start(function() scheduler.co_sleep(2000) print("Batch processing started") semaphore:post(5) -- 一次性释放 5 个信号 end) for i = 1, 5 do scheduler.co_start(function(id) local value = semaphore:wait(-1) print("Worker", id, "got batch signal, value:", value) end, i) end ``` ## co\_semaphore:name * 获取信号量名称 #### 函数原型 ::: tip API ```lua co_semaphore:name() ``` ::: #### 参数说明 此函数不需要参数。 #### 返回值说明 | 类型 | 描述 | |------|------| | string | 信号量名称字符串 | #### 用法说明 获取信号量的名称。 --- --- url: /posts/condition-and-select-compile.md --- xmake provides some built-in conditional judgment APIs for obtaining relevant information about project status during selective compilation to adjust compilation logic. For example: `is_os`, `is_plat`, `is_arch`, `is_kind`, `is_mode`, `is_option` ### `is_mode` Let's first talk about how to use the most commonly used `is_mode`. This API is mainly used to judge the current compilation mode. For example, when configuring compilation normally, you will execute: ```bash $ xmake f -m debug $ xmake ``` To compile the `debug` version, then the mode is `debug`. For the `release` version, it's `release`: ```bash $ xmake f -m release $ xmake ``` But if it's just configured like this, xmake still doesn't know how to compile for debug or how to compile the release version, because these mode values are not built-in. We can set them arbitrarily, for example: profile, checking, etc., to compile performance mode, detection mode. These depend on the actual needs of our project. Generally, only `debug` and `release` are needed. How to distinguish them? This needs to be configured in `xmake.lua`. Generally, you can refer to the following configuration: ```lua -- If current compilation mode is debug if is_mode("debug") then -- Add DEBUG compilation macro add_defines("DEBUG") -- Enable debug symbols set_symbols("debug") -- Disable optimization set_optimize("none") -- If it's release mode elseif is_mode("release") then -- Hide symbols set_symbols("hidden") -- Strip all symbols set_strip("all") -- Enable optimization to: fastest speed mode set_optimize("fastest") -- Ignore frame pointer add_cxflags("-fomit-frame-pointer") add_mxflags("-fomit-frame-pointer") end ``` By judging whether compiling the debug version, enable and disable debug symbol information, and judge whether to disable and enable optimization. Of course, if our project also sets other modes, such as performance analysis mode: profile, then we can also use this to judge whether to add some analysis compilation options. ### `is_plat` Next, let's talk about compilation platform judgment. This is also very practical. Although our tool is for cross-platform development, usually the configuration is definitely universal. But after all, there are thousands of projects with different needs. There will always be some projects that need to do special compilation processing for different platforms. At this time, we need this API, for example: ```lua -- If current platform is android if is_plat("android") then add_files("src/xxx/*.c") end -- If current platform is macosx or iphoneos if is_plat("macosx", "iphoneos") then add_mxflags("-framework Foundation") add_ldflags("-framework Foundation") end ``` Here, for the android platform, some special code compilation is added. For macosx and iphoneos platforms, Foundation framework linking is added. There's also a practical little trick here. The `is_xxx` series of interfaces can all pass multiple parameters at the same time, and the logic is an OR relationship. We can write it like above: ```lua if is_plat("macosx", "iphoneos", "android", "linux") then end ``` Otherwise, if using lua's native syntax, although it can also work, it will be very bloated, for example: ```lua if is_plat("macosx") or is_plat("iphoneos") or is_plat("android") or is_plat("linux") then end ``` In addition to the `is_xxx` series, APIs with plural suffixes like `s` such as `add_xxxs` can all pass multiple parameters, for example `add_files`: ```lua add_files("src/*.c", "test.c", "hello.cpp") ``` And so on, I won't introduce them one by one here. ### `is_arch` This is similar to `is_plat`, but it's used to judge the current compilation target architecture, which is: ```bash xmake f --arch=x86_64 ``` Then, we judge in the project description: ```lua -- If current architecture is x86_64 or i386 if is_arch("x86_64", "i386") then add_files("src/xxx/*.c") end -- If current platform is armv7, arm64, armv7s, armv7-a if is_arch("armv7", "arm64", "armv7s", "armv7-a") then -- ... end ``` If judging all arm architectures one by one like above, it might be very tedious. After all, each platform has many architecture types. xmake provides wildcard matching patterns similar to `add_files` to make judgments more concisely: ```lua -- If current platform is arm platform if is_arch("arm*") then -- ... end ``` Using `*` can match all. ### `is_os` This is simple, used to judge the current compilation target, for example: ```lua -- If current operating system is ios if is_os("ios") then add_files("src/xxx/*.m") end ``` Currently supported operating systems are: windows, linux, android, macosx, ios ### `is_kind` Used to judge whether currently compiling a dynamic library or static library. Generally used in the following scenarios: ```lua target("test") -- Set target's kind through configuration set_kind("$(kind)") add_files("src/*c") -- If currently compiling a static library, then add specified files if is_kind("static") then add_files("src/xxx.c") end ``` When configuring compilation, you can manually switch compilation types: ```lua -- Compile static library xmake f -k static xmake -- Compile dynamic library xmake f -k shared xmake ``` ### `is_option` If an auto-detection option or manually set option is enabled, you can judge through the `is_option` interface, for example: ```lua -- If manually enabled xmake f --demo=y option if is_option("demo") then -- Compile code in demo directory add_subdirs("src/demo") end ``` --- --- url: /api/scripts/builtin-modules/signal.md --- # signal 2.9.1 adds a new signal registration interface. We can register signal processing functions such as SIGINT in the Lua layer to customize the response logic. ## signal.register * Register signal handler #### Function Prototype ::: tip API ```lua signal.register(signo: , handler: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | signo | Signal number (e.g., signal.SIGINT) | | handler | Signal handler function | #### Usage Currently, it only supports SIGINT signal processing, and it also supports mainstream platforms such as windows. ```lua import("core.base.signal") function main() signal.register(signal.SIGINT, function (signo) print("signal.SIGINT(%d)", signo) end) io.read() end ``` This is useful when some sub-processes internally shield SIGINT, causing them to freeze and not exit. Even if the user presses `Ctrl+C` to exit the xmake process, it does not exit. We can force it out in this way. ```lua import("core.base.process") import("core.base.signal") function main() local proc signal.register(signal.SIGINT, function (signo) print("sigint") if proc then proc:kill() end end) proc = process.open("./trap.sh") if proc then proc:wait() proc:close() end end ``` For the background of this issue, please refer to: [#4889](https://github.com/xmake-io/xmake/issues/4889) ## signal.ignore * Ignore a signal #### Function Prototype ::: tip API ```lua signal.ignore(signo: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | signo | Signal number (e.g., signal.SIGINT) | #### Usage We can also ignore the processing of blocking a certain signal through the `signal.ignore` interface. ```lua signal.ignore(signal.SIGINT) ``` ## signal.reset * Reset a signal #### Function Prototype ::: tip API ```lua signal.reset(signo: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | signo | Signal number (e.g., signal.SIGINT) | #### Usage We can also clear the processing function of a certain signal and fall back to the default processing logic. ```lua signal.reset(signal.SIGINT) ``` --- --- url: /zh/api/scripts/builtin-modules/signal.md --- # signal 2.9.1 新增了信号注册接口,我们可以在 lua 层,注册 SIGINT 等信号处理函数,来定制化响应逻辑。 ## signal.register * 注册信号处理器 #### 函数原型 ::: tip API ```lua signal.register(signo: , handler: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | signo | 信号编号(例如:signal.SIGINT) | | handler | 信号处理函数 | #### 用法说明 目前仅仅支持 SIGINT 信号的处理,同时它也是支持 windows 等主流平台的。 ```lua import("core.base.signal") function main() signal.register(signal.SIGINT, function (signo) print("signal.SIGINT(%d)", signo) end) io.read() end ``` 这对于当一些子进程内部屏蔽了 SIGINT,导致卡死不退出,即使用户按了 `Ctrl+C` 退出了 xmake 进程,它也没有退出时候, 我们就可以通过这种方式去强制退掉它。 ```lua import("core.base.process") import("core.base.signal") function main() local proc signal.register(signal.SIGINT, function (signo) print("sigint") if proc then proc:kill() end end) proc = process.open("./trap.sh") if proc then proc:wait() proc:close() end end ``` 关于这个问题的背景,可以参考:[#4889](https://github.com/xmake-io/xmake/issues/4889) ## signal.ignore * 忽略某个信号 #### 函数原型 ::: tip API ```lua signal.ignore(signo: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | signo | 信号编号(例如:signal.SIGINT) | #### 用法说明 我们也可以通过 `signal.ignore` 这个接口,去忽略屏蔽某个信号的处理。 ```lua signal.ignore(signal.SIGINT) ``` ## signal.reset * 重置某个信号 #### 函数原型 ::: tip API ```lua signal.reset(signo: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | signo | 信号编号(例如:signal.SIGINT) | #### 用法说明 我们也可以清除某个信号的处理函数,回退到默认的处理逻辑。 ```lua signal.reset(signal.SIGINT) ``` --- --- url: /api/scripts/extension-modules/core/base/socket.md --- # socket The socket module provides cross-platform network socket functionality, supporting TCP, UDP, and Unix domain sockets. This is an extension module of xmake. ::: tip TIP To use this module, you need to import it first: `import("core.base.socket")` ::: ## socket.tcp * Create a TCP socket #### Function Prototype ::: tip API ```lua socket.tcp(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters | Options: * `family`: Address family, options: * `socket.IPV4` (1) - IPv4 address family (default) * `socket.IPV6` (2) - IPv6 address family #### Usage Creates a TCP socket object (`socket.TCP`), using IPv4 by default. TCP is a connection-oriented, reliable stream protocol that guarantees data arrives in order, suitable for most network communication scenarios. ```lua -- Create IPv4 TCP socket local sock = socket.tcp() -- Create IPv6 TCP socket local sock = socket.tcp({family = socket.IPV6}) ``` ## socket.udp * Create a UDP socket #### Function Prototype ::: tip API ```lua socket.udp(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters | Options: * `family`: Address family, can be `socket.IPV4` (default) or `socket.IPV6` #### Usage Creates a UDP socket object (`socket.UDP`) for connectionless datagram communication. UDP is a connectionless, unreliable datagram protocol that doesn't guarantee data arrival or order, but has low latency, suitable for real-time communication, broadcasting, etc. UDP is suitable for scenarios requiring low latency and can tolerate some packet loss: ```lua import("core.base.socket") import("core.base.bytes") local sock = socket.udp() sock:bind("127.0.0.1", 9091) local buff = bytes(8192) -- Receive datagram local recv, data, peer_addr, peer_port = sock:recvfrom(buff, 8192) if recv > 0 then print("Received", recv, "bytes from", peer_addr, peer_port) end sock:close() ``` ## socket.unix * Create a Unix domain socket #### Function Prototype ::: tip API ```lua socket.unix() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Creates a Unix domain socket (address family `socket.UNIX`) for inter-process communication on the same machine. Unix domain sockets use filesystem paths instead of IP addresses and ports, offering better performance than TCP because they don't require network protocol stack processing. Only available on Unix/Linux/macOS systems. Suitable for high-performance local inter-process communication. ## socket.bind * Create and bind a TCP socket #### Function Prototype ::: tip API ```lua socket.bind(addr: , port: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | addr | Required. IP address, such as "127.0.0.1" or "0.0.0.0" | | port | Required. Port number | | opt | Optional. Option parameters, same as socket.tcp | #### Usage Creates a TCP socket and binds it to the specified address and port, typically used for servers. Complete TCP echo server example: ```lua import("core.base.socket") import("core.base.bytes") function main() -- Bind address and listen local server = socket.bind("127.0.0.1", 9091) server:listen(20) print("Server listening on 127.0.0.1:9091") while true do -- Accept client connection local client = server:accept() if client then print("Client connected") local buff = bytes(8192) -- Continuously receive and echo data while true do local recv, data = client:recv(buff, 8192, {block = true}) if recv > 0 then print("Received:", data:str()) -- Echo data back client:send(data, {block = true}) else break end end client:close() end end server:close() end ``` ## socket.bind\_unix * Create and bind a Unix domain socket #### Function Prototype ::: tip API ```lua socket.bind_unix(addr: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | addr | Required. Unix domain socket path | | opt | Optional. Option parameters | Options: * `is_abstract`: Whether to use abstract namespace (Linux only) #### Usage Creates a Unix domain socket and binds it to the specified path. ```lua import("core.base.socket") -- Bind to file path local server = socket.bind_unix("/tmp/my.sock") server:listen(10) ``` ## socket.connect * Create and connect a TCP socket #### Function Prototype ::: tip API ```lua socket.connect(addr: , port: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | addr | Required. Server IP address | | port | Required. Server port number | | opt | Optional. Option parameters | Options: * `family`: Address family * `timeout`: Connection timeout (milliseconds) #### Usage Creates a TCP socket and connects to the specified address and port, used for clients. Complete TCP client example: ```lua import("core.base.socket") import("core.base.bytes") function main() -- Connect to server local sock = socket.connect("127.0.0.1", 9091) if sock then print("Connected to server") local buff = bytes(8192) -- Send multiple messages local count = 0 while count < 1000 do local send = sock:send("hello world..", {block = true}) if send > 0 then -- Receive echo sock:recv(buff, 13, {block = true}) count = count + 1 else break end end print("Sent successfully, count:", count) sock:close() end end ``` ## socket.connect\_unix * Create and connect a Unix domain socket #### Function Prototype ::: tip API ```lua socket.connect_unix(addr: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | addr | Required. Unix domain socket path | | opt | Optional. Option parameters | Options: * `is_abstract`: Whether to use abstract namespace (Linux only) * `timeout`: Connection timeout #### Usage Creates a Unix domain socket and connects to the specified path. ## socket:bind * Bind socket to address #### Function Prototype ::: tip API ```lua socket:bind(addr: , port: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | addr | Required. IP address | | port | Required. Port number | #### Return Value | Type | Description | |------|-------------| | number | Returns a positive number | #### Usage Binds the socket to the specified IP address and port. ## socket:listen * Start listening for connections #### Function Prototype ::: tip API ```lua socket:listen(backlog: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | backlog | Optional. Maximum length of the pending connection queue, default 10 | #### Usage Makes the socket start listening for client connections, used for servers. Must be called after `bind` and before `accept`. ## socket:accept * Accept client connection #### Function Prototype ::: tip API ```lua socket:accept(opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | opt | Optional. Option parameters | Options: * `timeout`: Timeout (milliseconds), default -1 (infinite wait) #### Return Value | Type | Description | |------|-------------| | socket | Returns client socket object | #### Usage Accepts a client connection, returns a new socket object for communicating with the client. Non-blocking by default, returns immediately if no client is connecting. Can be used with [sock:wait](#sock-wait) for event-driven approach: ```lua -- Wait for client connection local events = server:wait(socket.EV_ACPT, 5000) if events == socket.EV_ACPT then local client = server:accept() end ``` ## socket:connect * Connect to remote address #### Function Prototype ::: tip API ```lua socket:connect(addr: , port: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | addr | Required. Target IP address | | port | Required. Target port number | | opt | Optional. Option parameters | Options: * `timeout`: Connection timeout (milliseconds) #### Return Value | Type | Description | |------|-------------| | number | Returns a positive number | #### Usage Connects to the specified remote address and port. ## socket:send * Send data #### Function Prototype ::: tip API ```lua socket:send(data: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Data to send, can be string or bytes object | | opt | Optional. Option parameters | Options: * `block`: Whether to block sending, default false * `start`: Data start position, default 1 * `last`: Data end position, default is data size #### Return Value | Type | Description | |------|-------------| | number | Actual number of bytes sent | #### Usage Sends data through the socket. Non-blocking mode may only send partial data, blocking mode waits until all data is sent: ```lua -- Non-blocking send local sent = sock:send("hello") -- Blocking send, ensure all sent local sent = sock:send("hello world", {block = true}) if sent > 0 then print("Sent", sent, "bytes") end ``` ## socket:recv * Receive data #### Function Prototype ::: tip API ```lua socket:recv(buff: , size: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | buff | Required. bytes buffer object | | size | Required. Number of bytes to receive | | opt | Optional. Option parameters | Options: * `block`: Whether to block receiving, default false * `timeout`: Timeout (milliseconds) #### Return Values | Type | Description | |------|-------------| | recv | Actual number of bytes received | | data | Received data (bytes object) | #### Usage Receives data from the socket. ```lua import("core.base.bytes") local buff = bytes(8192) -- Non-blocking receive local recv, data = sock:recv(buff, 1024) -- Blocking receive, timeout 5 seconds local recv, data = sock:recv(buff, 1024, {block = true, timeout = 5000}) if recv > 0 then print("Received:", data:str()) end ``` ## socket:sendto * Send datagram (UDP) #### Function Prototype ::: tip API ```lua socket:sendto(data: , addr: , port: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | data | Required. Data to send, can be string or bytes object | | addr | Required. Target IP address | | port | Required. Target port number | | opt | Optional. Option parameters | #### Return Value | Type | Description | |------|-------------| | number | Actual number of bytes sent | #### Usage Sends a datagram to the specified address via UDP socket. ```lua import("core.base.socket") local sock = socket.udp() sock:sendto("hello", "127.0.0.1", 9091) sock:close() ``` ## socket:recvfrom * Receive datagram (UDP) #### Function Prototype ::: tip API ```lua socket:recvfrom(buff: , size: , opt:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | buff | Required. bytes buffer object | | size | Required. Number of bytes to receive | | opt | Optional. Option parameters | Options: * `block`: Whether to block receiving #### Return Values | Type | Description | |------|-------------| | recv | Actual number of bytes received | | data | Received data (bytes object) | | peer\_addr | Sender's IP address | | peer\_port | Sender's port number | #### Usage Receives a datagram from the UDP socket and gets the sender's address information. Complete UDP echo server example: ```lua import("core.base.socket") import("core.base.bytes") function main() local sock = socket.udp() sock:bind("127.0.0.1", 9091) print("UDP server listening on 127.0.0.1:9091") local buff = bytes(8192) while true do print("Waiting to receive data...") local recv, data, peer_addr, peer_port = sock:recvfrom(buff, 8192, {block = true}) if recv > 0 then print("Received", recv, "bytes from", peer_addr .. ":" .. peer_port .. ":", data:str()) -- Echo data back sock:sendto(data, peer_addr, peer_port) end end sock:close() end ``` ## socket:wait * Wait for socket events #### Function Prototype ::: tip API ```lua socket:wait(events: , timeout: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | events | Required. Events to wait for, supports the following event constants | | timeout | Required. Timeout (milliseconds), -1 means infinite wait | Supported event constants: * `socket.EV_RECV` (1): Receivable event * `socket.EV_SEND` (2): Sendable event * `socket.EV_CONN` (2): Connection event (equivalent to EV\_SEND) * `socket.EV_ACPT` (1): Accept connection event (equivalent to EV\_RECV) #### Return Value | Type | Description | |------|-------------| | number | Returns the actual event constant value that occurred | #### Usage Waits for specified socket events to occur. Implementing event-driven in non-blocking mode: ```lua -- Wait for socket to be readable local events = sock:wait(socket.EV_RECV, 1000) if events == socket.EV_RECV then local recv, data = sock:recv(buff, 1024) end -- Wait for socket to be writable local events = sock:wait(socket.EV_SEND, 1000) if events == socket.EV_SEND then sock:send("data") end ``` ## socket:close * Close socket #### Function Prototype ::: tip API ```lua socket:close() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Closes the socket and releases resources. Sockets should be closed promptly after use. ## socket:ctrl * Control socket options #### Function Prototype ::: tip API ```lua socket:ctrl(code: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | code | Required. Control code constant | | value | Required. Control value | Supported control code constants: * `socket.CTRL_SET_RECVBUFF` (2): Set receive buffer size (bytes) * `socket.CTRL_SET_SENDBUFF` (4): Set send buffer size (bytes) #### Usage Sets socket control options to adjust socket parameters such as buffer sizes. Increasing buffer sizes can improve performance in high-throughput scenarios: ```lua -- Set receive buffer to 64KB sock:ctrl(socket.CTRL_SET_RECVBUFF, 65536) -- Set send buffer to 64KB sock:ctrl(socket.CTRL_SET_SENDBUFF, 65536) ``` ::: tip TIP Sockets are non-blocking by default. Use the `{block = true}` option to enable blocking mode for simpler programming. In a coroutine environment, sockets automatically integrate with the scheduler for asynchronous I/O. ::: ::: warning WARNING Remember to call `close()` after using the socket to release resources. A bytes buffer must be created using `bytes()` before receiving data. ::: --- --- url: /zh/api/scripts/extension-modules/core/base/socket.md --- # socket socket 模块提供了跨平台的网络套接字功能,支持 TCP、UDP 和 Unix 域套接字。这是 xmake 的扩展模块。 ::: tip 提示 使用此模块需要先导入:`import("core.base.socket")` ::: ## socket.tcp * 创建 TCP 套接字 #### 函数原型 ::: tip API ```lua socket.tcp(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数 | #### 用法说明 创建一个 TCP 套接字对象(`socket.TCP`),默认使用 IPv4。 TCP 是面向连接的、可靠的流式协议,保证数据按序到达,适合大多数网络通信场景。 `opt` 选项: * `family`:地址族,可选值: * `socket.IPV4` (1) - IPv4 地址族(默认) * `socket.IPV6` (2) - IPv6 地址族 ```lua -- 创建 IPv4 TCP 套接字 local sock = socket.tcp() -- 创建 IPv6 TCP 套接字 local sock = socket.tcp({family = socket.IPV6}) ``` ## socket.udp * 创建 UDP 套接字 #### 函数原型 ::: tip API ```lua socket.udp(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数 | #### 用法说明 创建一个 UDP 套接字对象(`socket.UDP`),用于无连接的数据报通信。 UDP 是无连接的、不可靠的数据报协议,不保证数据到达和顺序,但延迟低,适合实时通信、广播等场景。 `opt` 选项: * `family`:地址族,可选值为 `socket.IPV4`(默认)或 `socket.IPV6` UDP 适合需要低延迟、可以容忍少量丢包的场景: ```lua import("core.base.socket") import("core.base.bytes") local sock = socket.udp() sock:bind("127.0.0.1", 9091) local buff = bytes(8192) -- 接收数据报 local recv, data, peer_addr, peer_port = sock:recvfrom(buff, 8192) if recv > 0 then print("从", peer_addr, peer_port, "收到", recv, "字节") end sock:close() ``` ## socket.unix * 创建 Unix 域套接字 #### 函数原型 ::: tip API ```lua socket.unix() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 创建一个 Unix 域套接字(地址族为 `socket.UNIX`),用于同一台机器上的进程间通信。 Unix 域套接字使用文件系统路径而不是 IP 地址和端口,性能优于 TCP,因为不需要网络协议栈处理。 仅在 Unix/Linux/macOS 系统上可用。适合本地进程间的高性能通信。 ## socket.bind * 创建并绑定 TCP 套接字 #### 函数原型 ::: tip API ```lua socket.bind(addr: , port: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | addr | 必需。IP 地址,如 "127.0.0.1" 或 "0.0.0.0" | | port | 必需。端口号 | | opt | 可选。选项参数,同 socket.tcp | #### 用法说明 创建 TCP 套接字并绑定到指定地址和端口,通常用于服务端。 完整的 TCP 回显服务器示例: ```lua import("core.base.socket") import("core.base.bytes") function main() -- 绑定地址并监听 local server = socket.bind("127.0.0.1", 9091) server:listen(20) print("服务器监听在 127.0.0.1:9091") while true do -- 接受客户端连接 local client = server:accept() if client then print("客户端已连接") local buff = bytes(8192) -- 持续接收和回显数据 while true do local recv, data = client:recv(buff, 8192, {block = true}) if recv > 0 then print("收到:", data:str()) -- 回显数据 client:send(data, {block = true}) else break end end client:close() end end server:close() end ``` ## socket.bind\_unix * 创建并绑定 Unix 域套接字 #### 函数原型 ::: tip API ```lua socket.bind_unix(addr: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | addr | 必需。Unix 域套接字路径 | | opt | 可选。选项参数 | #### 用法说明 创建 Unix 域套接字并绑定到指定路径。 `opt` 选项: * `is_abstract`:是否使用抽象命名空间(仅 Linux) ```lua import("core.base.socket") -- 绑定到文件路径 local server = socket.bind_unix("/tmp/my.sock") server:listen(10) ``` ## socket.connect * 创建并连接 TCP 套接字 #### 函数原型 ::: tip API ```lua socket.connect(addr: , port: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | addr | 必需。服务器 IP 地址 | | port | 必需。服务器端口号 | | opt | 可选。选项参数 | #### 用法说明 创建 TCP 套接字并连接到指定地址和端口,用于客户端。 `opt` 选项: * `family`:地址族 * `timeout`:连接超时时间(毫秒) 完整的 TCP 客户端示例: ```lua import("core.base.socket") import("core.base.bytes") function main() -- 连接到服务器 local sock = socket.connect("127.0.0.1", 9091) if sock then print("已连接到服务器") local buff = bytes(8192) -- 发送多条消息 local count = 0 while count < 1000 do local send = sock:send("hello world..", {block = true}) if send > 0 then -- 接收回显 sock:recv(buff, 13, {block = true}) count = count + 1 else break end end print("发送成功,count:", count) sock:close() end end ``` ## socket.connect\_unix * 创建并连接 Unix 域套接字 #### 函数原型 ::: tip API ```lua socket.connect_unix(addr: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | addr | 必需。Unix 域套接字路径 | | opt | 可选。选项参数 | #### 用法说明 创建 Unix 域套接字并连接到指定路径。 `opt` 选项: * `is_abstract`:是否使用抽象命名空间(仅 Linux) * `timeout`:连接超时时间 ## socket:bind * 绑定套接字到地址 #### 函数原型 ::: tip API ```lua socket:bind(addr: , port: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | addr | 必需。IP 地址 | | port | 必需。端口号 | #### 返回值说明 | 类型 | 描述 | |------|------| | number | 成功返回正数 | #### 用法说明 将套接字绑定到指定的 IP 地址和端口。 ## socket:listen * 开始监听连接 #### 函数原型 ::: tip API ```lua socket:listen(backlog: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | backlog | 必需。等待连接队列的最大长度,默认 10 | #### 用法说明 使套接字开始监听客户端连接,用于服务端。 必须在 `bind` 之后、`accept` 之前调用。 ## socket:accept * 接受客户端连接 #### 函数原型 ::: tip API ```lua socket:accept(opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | opt | 可选。选项参数 | `opt` 选项: * `timeout`:超时时间(毫秒),默认 -1(无限等待) #### 返回值说明 | 类型 | 描述 | |------|------| | socket | 成功返回客户端套接字对象 | #### 用法说明 接受一个客户端连接,返回新的套接字对象用于与客户端通信。默认是非阻塞的,如果没有客户端连接会立即返回。可配合 [sock:wait](#sock-wait) 实现事件驱动: ```lua -- 等待客户端连接 local events = server:wait(socket.EV_ACPT, 5000) if events == socket.EV_ACPT then local client = server:accept() end ``` ## socket:connect * 连接到远程地址 #### 函数原型 ::: tip API ```lua socket:connect(addr: , port: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | addr | 必需。目标 IP 地址 | | port | 必需。目标端口号 | | opt | 可选。选项参数 | #### 用法说明 连接到指定的远程地址和端口。 `opt` 选项: * `timeout`:连接超时时间(毫秒) #### 返回值说明 | 类型 | 描述 | |------|------| | number | 成功返回正数 | ## socket:send * 发送数据 #### 函数原型 ::: tip API ```lua socket:send(data: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。要发送的数据,可以是字符串或 bytes 对象 | | opt | 可选。选项参数 | #### 用法说明 通过套接字发送数据。 `opt` 选项: * `block`:是否阻塞发送,默认 false * `start`:数据起始位置,默认 1 * `last`:数据结束位置,默认为数据大小 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 实际发送的字节数 | 非阻塞模式可能只发送部分数据,阻塞模式会等待直到所有数据发送完成: ```lua -- 非阻塞发送 local sent = sock:send("hello") -- 阻塞发送,确保全部发送 local sent = sock:send("hello world", {block = true}) if sent > 0 then print("发送了", sent, "字节") end ``` ## socket:recv * 接收数据 #### 函数原型 ::: tip API ```lua socket:recv(buff: , size: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | buff | 必需。bytes 缓冲区对象 | | size | 必需。要接收的字节数 | | opt | 可选。选项参数 | #### 用法说明 从套接字接收数据。 `opt` 选项: * `block`:是否阻塞接收,默认 false * `timeout`:超时时间(毫秒) #### 返回值说明 | 类型 | 描述 | |------|------| | recv | 实际接收的字节数 | | data | 接收的数据(bytes 对象) | ```lua import("core.base.bytes") local buff = bytes(8192) -- 非阻塞接收 local recv, data = sock:recv(buff, 1024) -- 阻塞接收,超时 5 秒 local recv, data = sock:recv(buff, 1024, {block = true, timeout = 5000}) if recv > 0 then print("接收到:", data:str()) end ``` ## socket:sendto * 发送数据报(UDP) #### 函数原型 ::: tip API ```lua socket:sendto(data: , addr: , port: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | data | 必需。要发送的数据,可以是字符串或 bytes 对象 | | addr | 必需。目标 IP 地址 | | port | 必需。目标端口号 | | opt | 可选。选项参数 | #### 用法说明 通过 UDP 套接字发送数据报到指定地址。 #### 返回值说明 | 类型 | 描述 | |------|------| | number | 实际发送的字节数 | ```lua import("core.base.socket") local sock = socket.udp() sock:sendto("hello", "127.0.0.1", 9091) sock:close() ``` ## socket:recvfrom * 接收数据报(UDP) #### 函数原型 ::: tip API ```lua socket:recvfrom(buff: , size: , opt:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | buff | 必需。bytes 缓冲区对象 | | size | 必需。要接收的字节数 | | opt | 可选。选项参数 | #### 用法说明 从 UDP 套接字接收数据报,同时获取发送方的地址信息。 `opt` 选项: * `block`:是否阻塞接收 #### 返回值说明 | 类型 | 描述 | |------|------| | recv | 实际接收的字节数 | | data | 接收的数据(bytes 对象) | | peer\_addr | 发送方 IP 地址 | | peer\_port | 发送方端口号 | 完整的 UDP 回显服务器示例: ```lua import("core.base.socket") import("core.base.bytes") function main() local sock = socket.udp() sock:bind("127.0.0.1", 9091) print("UDP 服务器监听在 127.0.0.1:9091") local buff = bytes(8192) while true do print("等待接收数据...") local recv, data, peer_addr, peer_port = sock:recvfrom(buff, 8192, {block = true}) if recv > 0 then print("从", peer_addr .. ":" .. peer_port, "收到", recv, "字节:", data:str()) -- 回显数据 sock:sendto(data, peer_addr, peer_port) end end sock:close() end ``` ## socket:wait * 等待套接字事件 #### 函数原型 ::: tip API ```lua socket:wait(events: , timeout: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | events | 必需。要等待的事件 | | timeout | 必需。超时时间(毫秒),-1 表示无限等待 | #### 用法说明 等待指定的套接字事件发生。 支持的事件常量: * `socket.EV_RECV` (1):可接收事件 * `socket.EV_SEND` (2):可发送事件 * `socket.EV_CONN` (2):连接事件(等同于 EV\_SEND) * `socket.EV_ACPT` (1):接受连接事件(等同于 EV\_RECV) #### 返回值说明 | 类型 | 描述 | |------|------| | number | 返回实际发生的事件常量值 | #### 用法说明 等待指定的套接字事件发生。在非阻塞模式下实现事件驱动: ```lua -- 等待套接字可读 local events = sock:wait(socket.EV_RECV, 1000) if events == socket.EV_RECV then local recv, data = sock:recv(buff, 1024) end -- 等待套接字可写 local events = sock:wait(socket.EV_SEND, 1000) if events == socket.EV_SEND then sock:send("data") end ``` ## socket:close * 关闭套接字 #### 函数原型 ::: tip API ```lua socket:close() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 关闭套接字并释放资源。使用完套接字后应及时关闭。 ## socket:ctrl * 控制套接字选项 #### 函数原型 ::: tip API ```lua socket:ctrl(code: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | code | 必需。控制码常量 | | value | 必需。控制值 | #### 用法说明 设置套接字的控制选项,用于调整套接字的缓冲区等参数。 支持的控制码常量: * `socket.CTRL_SET_RECVBUFF` (2):设置接收缓冲区大小(字节) * `socket.CTRL_SET_SENDBUFF` (4):设置发送缓冲区大小(字节) 增大缓冲区可以提高高吞吐量场景下的性能: ```lua -- 设置接收缓冲区为 64KB sock:ctrl(socket.CTRL_SET_RECVBUFF, 65536) -- 设置发送缓冲区为 64KB sock:ctrl(socket.CTRL_SET_SENDBUFF, 65536) ``` ::: tip 提示 套接字默认是非阻塞的。使用 `{block = true}` 选项可以启用阻塞模式,简化编程。在协程环境中,套接字会自动与调度器集成,实现异步 I/O。 ::: ::: warning 注意 使用完套接字后记得调用 `close()` 释放资源。接收数据时需要预先使用 `bytes()` 创建缓冲区。 ::: --- --- url: /api/description/specification.md --- # Specification ## Naming conventions The interface is named according to some of the predefined specifications, which is more convenient to understand and easy to use. It's according to the following rules: | Interfaces | Description | | --------------------- | ---------------------------------------------------------------- | | `is_`/`has_` + xxx | Condition interfaces | | `set_` + xxx | Set and override the previous settings | | `add_` + xxx | Set and append settings | | `…s` + xxx *(plural)* | Support multi-parameters, .e.g:`add_files("*.c", "test.cpp")` | | `on_` + xxx | Set and override builtin script | | `before_` + xxx | Set and run this script before running builtin-script | | `after_` + xxx | Set and run this script after running builtin-script | | `scope("name")` | Define a description scope, .e.g `target("xxx")`, `option("xxx")`| | scope/settings | Indentation with spaces | --- --- url: /api/scripts/builtin-modules/string.md --- # string The string module is a native module of lua. For details, see: [lua official manual](https://www.lua.org/manual/5.1/manual.html#5.4) It has been extended in xmake to add some extension interfaces: ## string.startswith * Determine if the beginning of the string matches #### Function Prototype ::: tip API ```lua string.startswith(str: , prefix: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | str | String to check | | prefix | Prefix string to match | #### Usage ```lua local s = "hello xmake" if s:startswith("hello") then print("match") end ``` ## string.endswith * Determine if the end of the string matches #### Function Prototype ::: tip API ```lua string.endswith(str: , suffix: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | str | String to check | | suffix | Suffix string to match | #### Usage ```lua local s = "hello xmake" if s:endswith("xmake") then print("match") end ``` ## string.split * Split string by separator #### Function Prototype ::: tip API ```lua string.split(str: , separator: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | str | String to split | | separator | Separator string | | options | Split options table (optional) | #### Usage pattern match and ignore empty string ```lua ("1\n\n2\n3"):split('\n') => 1, 2, 3 ("abc123123xyz123abc"):split('123') => abc, xyz, abc ("abc123123xyz123abc"):split('[123]+') => abc, xyz, abc ``` plain match and ignore empty string ```lua ("1\n\n2\n3"):split('\n', {plain = true}) => 1, 2, 3 ("abc123123xyz123abc"):split('123', {plain = true}) => abc, xyz, abc ``` pattern match and contains empty string ```lua ("1\n\n2\n3"):split('\n', {strict = true}) => 1, , 2, 3 ("abc123123xyz123abc"):split('123', {strict = true}) => abc, , xyz, abc ("abc123123xyz123abc"):split('[123]+', {strict = true}) => abc, xyz, abc ``` plain match and contains empty string ```lua ("1\n\n2\n3"):split('\n', {plain = true, strict = true}) => 1, , 2, 3 ("abc123123xyz123abc"):split('123', {plain = true, strict = true}) => abc, , xyz, abc ``` limit split count ```lua ("1\n\n2\n3"):split('\n', {limit = 2}) => 1, 2\n3 ("1.2.3.4.5"):split('%.', {limit = 3}) => 1, 2, 3.4.5 ``` ## string.trim * Remove the left and right whitespace characters of the string #### Function Prototype ::: tip API ```lua string.trim(str: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | str | String to trim | #### Usage ```lua string.trim(" hello xmake! ") ``` The result is: "hello xmake!" ## string.ltrim * Remove the whitespace character to the left of the string #### Function Prototype ::: tip API ```lua string.ltrim(str: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | str | String to trim | #### Usage ```lua string.ltrim(" hello xmake! ") ``` The result is: "hello xmake! " ## string.rtrim * Remove the whitespace character to the right of the string #### Function Prototype ::: tip API ```lua string.rtrim(str: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | str | String to trim | #### Usage ```lua string.rtrim(" hello xmake! ") ``` The result is: " hello xmake!" --- --- url: /zh/api/scripts/builtin-modules/string.md --- # string 字符串模块为lua原生自带的模块,具体使用见:[lua官方手册](https://www.lua.org/manual/5.1/manual.html#5.4) Xmake 中对其进行了扩展,增加了一些扩展接口: ## string.startswith * 判断字符串开头是否匹配 #### 函数原型 ::: tip API ```lua string.startswith(str: , prefix: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | str | 要检查的字符串 | | prefix | 要匹配的前缀字符串 | #### 用法说明 ```lua local s = "hello xmake" if s:startswith("hello") then print("match") end ``` ## string.endswith * 判断字符串结尾是否匹配 #### 函数原型 ::: tip API ```lua string.endswith(str: , suffix: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | str | 要检查的字符串 | | suffix | 要匹配的后缀字符串 | #### 用法说明 ```lua local s = "hello xmake" if s:endswith("xmake") then print("match") end ``` ## string.split * 分割字符串 #### 函数原型 ::: tip API ```lua string.split(str: , separator: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | str | 要分割的字符串 | | separator | 分隔符字符串 | | options | 分割选项表(可选) | #### 用法说明 v2.2.7版本对这个接口做了改进,以下是对2.2.7之后版本的使用说明。 按模式匹配分割字符串,忽略空串,例如: ```lua ("1\n\n2\n3"):split('\n') => 1, 2, 3 ("abc123123xyz123abc"):split('123') => abc, xyz, abc ("abc123123xyz123abc"):split('[123]+') => abc, xyz, abc ``` 按纯文本匹配分割字符串,忽略空串(省去了模式匹配,会提升稍许性能),例如: ```lua ("1\n\n2\n3"):split('\n', {plain = true}) => 1, 2, 3 ("abc123123xyz123abc"):split('123', {plain = true}) => abc, xyz, abc ``` 按模式匹配分割字符串,严格匹配,不忽略空串,例如: ```lua ("1\n\n2\n3"):split('\n', {strict = true}) => 1, , 2, 3 ("abc123123xyz123abc"):split('123', {strict = true}) => abc, , xyz, abc ("abc123123xyz123abc"):split('[123]+', {strict = true}) => abc, xyz, abc ``` 按纯文本匹配分割字符串,严格匹配,不忽略空串(省去了模式匹配,会提升稍许性能),例如: ```lua ("1\n\n2\n3"):split('\n', {plain = true, strict = true}) => 1, , 2, 3 ("abc123123xyz123abc"):split('123', {plain = true, strict = true}) => abc, , xyz, abc ``` 限制分割块数 ```lua ("1\n\n2\n3"):split('\n', {limit = 2}) => 1, 2\n3 ("1.2.3.4.5"):split('%.', {limit = 3}) => 1, 2, 3.4.5 ``` ## string.trim * 去掉字符串左右空白字符 #### 函数原型 ::: tip API ```lua string.trim(str: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | str | 要处理的字符串 | #### 用法说明 ```lua string.trim(" hello xmake! ") ``` 结果为:"hello xmake!" ## string.ltrim * 去掉字符串左边空白字符 #### 函数原型 ::: tip API ```lua string.ltrim(str: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | str | 要处理的字符串 | #### 用法说明 ```lua string.ltrim(" hello xmake! ") ``` 结果为:"hello xmake! " ## string.rtrim * 去掉字符串右边空白字符 #### 函数原型 ::: tip API ```lua string.rtrim(str: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | str | 要处理的字符串 | #### 用法说明 ```lua string.rtrim(" hello xmake! ") ``` 结果为:" hello xmake!" --- --- url: /zh/posts/xmake-summer-ospp.md --- ### "开源之夏" 简介 中科院软件所与华为 openEuler 社区去年共同举办了 “开源软件供应链点亮计划——暑期2020” 活动,今年为第二届。该活动旨在鼓励大家关注开源软件和开源社区,致力于培养和发掘更多优秀的开发者。 开源之夏网站: ### 来 Xmake 社区一起做项目 今年 Xmake 社区也报名参加了此活动,并且提供了三个活动项目,难易程度中等,欢迎大家一起参与进来,还有奖金可以领取哦。 Summer2021 活动为期 3 个月: Mentor 负责指导报名的 Student 完成并达成预期的目标 达成目标后,活动主办方会给与 Mentor 和 Student 一定的奖励和资助 数额因项目难度和完成情况而略有差异,从 6000 - 12000 不等,具体情况以开源之夏活动官网为准,解释权归活动主办方所有 ### 活动项目介绍 #### xmake 仓库包制作 难度:低 项目介绍: 在 xmake 的官方 C/C++ 仓库 中,新增提交 20 个常用的 C/C++ 库 其中,需要包括以下开发库: 1. matlab sdk 开发库 2. gtk3/glib 系列相关库 剩下的自己挑选一些 github star > 500 的 C/C++ 库提交收录。 项目产出要求: * 完成 matlab 相关库收录 * 完成 gtk 库收录 * 完成 glib 库收录 * 完成其他 C/C++ 库收录 相关资料文档: * [包制作和提交文档](https://xmake.io/zh/) * [包仓库地址](https://github.com/xmake-io/xmake-repo) #### 实现 xcode 工程生成插件 难度:中 项目介绍: 将 xmake.lua 维护的 C/C++ 项目,通过 `xmake project -k xcode` 命令,生成完整的 xcode 工程文件,并且能够在 xcode 中编译运行项目。 背景: xmake 当前版本也是有提供生成 xcode 工程生成的,但是是基于 cmake,先通过 `xmake project -k cmake` 在内部生成 CMakelist.txt,再去使用 cmake 生成的 xcodo 工程文件。 通过这种方式,增加了对 cmake 的依赖,并且不够灵活可控,受限于 cmake 的支持力度,很多 xmake 特有的功能特性都没法直接支持。 因此,需要重写一个完全新版本的插件,直接基于 xmake 的模块,读取 xmake.lua 工程信息来生成 xcode 工程。 项目产出要求: * 生成 xcode 工程文件 * 支持在 xcode 中编译运行 * 支持分组展示源文件树 * 支持 debug 和 release 等模式切换 相关资料文档: * [插件开发文档](https://xmake.io/zh/) * [现有插件代码目录](https://github.com/xmake-io/xmake/tree/master/xmake/plugins/project/xcode) #### 重构升级基于 xmake 的 IntelliJ IDEA 插件 难度:中 项目介绍: 原有插件项目地址:https://github.com/xmake-io/xmake-idea 原有的插件代码太老,Clion/Idea 等 2021 新版本已经完全不兼容,需要重构下。 并且需要在现有功能的基础上,增加 intelligense 和 断点调试支持。 项目产出要求: * 支持最新版 Idea/Clion IDE 2021 * 完全支持老插件的所有功能,创建,配置,编译和运行 * C/C++ Intelligense 支持 * xmake.lua 的自动补全 * 断点调试支持 相关的资料文档: * [老的项目地址](https://github.com/xmake-io/xmake-idea) * [相关的 Issues 讨论](https://github.com/xmake-io/xmake-idea/issues/5) * [参考其他插件: bazelbuild](https://github.com/bazelbuild/intellij) ### 导师联系方式 * 邮箱:waruqi@gmail.com * 微信:waruqi (备注:summber2021) * QQ 群:343118190 * Discord: * 直接 Issues 上沟通: --- --- url: /zh/posts/xmake-summer-2022.md --- ### "开源之夏" 简介 开源之夏是开源软件供应链点亮计划下的暑期活动,由中国科学院软件研究所与openEuler社区联合主办,旨在鼓励在校学生积极参与开源软件的开发维护,促进优秀开源软件社区的蓬勃发展。作为每年暑期最火热的开源活动,开源之夏如今已进入第三届。 2022年,开源之夏联合124家开源社区,针对开源项目的开发与维护提供mini任务,学生可自主选择感兴趣的项目进行申请,并在中选后获得社区资深开发者亲自指导的机会。项目成功结项并贡献给社区后,参与者将获得开源之夏活动奖金和结项证书。 开源之夏网站: ### 来 Xmake 社区一起做项目 Xmake 社区今年继续参加了开源之夏 2022 活动,欢迎年满 18 周岁的在校学生踊跃参与我们的项目,还有奖金拿哦。 ![xmake\_summer](https://tboox.org/static/img/xmake/xmake_summer.jpeg) ## Xmake 社区项目 学生可以申请参加以下三个项目之一,完整项目详情见:[Xmake 项目详情](https://summer-ospp.ac.cn/#/org/orgdetail/090748c6-6504-4d2d-9a11-f9f3e1876f7b/) * [Xmake 官网](https://xmake.io/zh/) ### Xmake 仓库包制作 完成制作下面 12 个 C/C++ 包进入 Xmake 官方包仓库 * https://github.com/wolfSSL/wolfssl * https://github.com/apache/apr * https://github.com/wmcbrine/PDCurses * https://github.com/grpc/grpc * https://github.com/zyantific/zydis * https://github.com/modm-io/modm * https://gitlab.gnome.org/GNOME/gdk-pixbuf * https://github.com/xtensor-stack/xtensor-io * https://github.com/AGWA/git-crypt * https://github.com/NVIDIA/thrust * v8 for windows * quickjs port for windows 导师:waruqi@gmail.com ### xmake-idea 插件改进 由于 Idea 更新迭代频繁,现有 xmake-idea 插件编译存在很多的废弃 API 使用警告,并且最新 CLion 版本也不再支持,因此需要做一些更新支持 并且需要新增一些配置选项用于支持最新版本的 Xmake 的配置 * 兼容最新版本的 CLion * 移除所有废弃的 API 使用,并且在不影响功能的前提下,使用新的 API 进行替代 * 配置面板增加一个工具链切换的配置选项 * 更新现有配置中,平台,架构的选项列表 * 检测 xmake.lua 改动自动更新生成 CMakeLists.txt 和 compile\_commands.json 文件 导师:dinophp@gmail.com ### 基于 Xmake 的面向 RT-Thread Smart 操作系统的系统构建工具 用于 RT-Thread Smart 开源操作系统的,基于 Xmake 的类 buildroot 的交叉构建系统:smart-build,它可以编译基础的软件包(调用xmake & xrepo的方式),构建出基本的应用程序,并输出相关文件到根文件系统文件夹下。 希望可以做到: * 针对系列的软件包,构建类似buildroot的menuconfig选择软件包及配置; * 支持两种以上架构的编译工具链,如arm、aarch64、risc-v等中的两种,并可选择; * 支持软件包的不同版本,并处理好依赖关系,并从网络上下载下来到本地; * 支持release模式编译,支持debug模式编译; * 支持按静态库模式编译,支持按动态库模式编译; * 支持在最终输出到根文件系统时strip掉多余的符号信息; 欢迎有更多自己的想法,或从用户端对这个事情的理解和考虑。 产出标准: * 能够基于RT-Thread Smart应用程序构建的方式,构建一个个的程序,并输出到 rt-smart/userapps/root 目录下,并可以使用已有脚本转成rootfs的映像文件; * 最终代码可以更新到git仓库中,代码符合xmake的规范; 技术要求: * 熟悉构建系统,熟悉基于GNU GCC & ld 的编译过程; * 熟悉lua语言,对 xmake、xrepo 有一定的了解; * 熟悉git操作; ## 中选学生可以获得什么 * 结识开源界小伙伴和技术大牛 * 获得社区导师的专业指导 * 获得开源项目的经验、经历,丰富个人简历 * 获得纪念品、奖金和证书: 如果想了解更多详情,见开源之夏官方网站。 * [Xmake 官网](https://xmake.io/zh/) * [Xmake 项目](https://github.com/xmake-io/xmake) * 开源之夏官网:https://summer-ospp.ac.cn/ * 学生参加指南:https://summer-ospp.ac.cn/help/student/ * Xmake 项目详情:https://summer-ospp.ac.cn/#/org/orgdetail/090748c6-6504-4d2d-9a11-f9f3e1876f7b/ --- --- url: /guide/basic-commands/switch-toolchains.md --- # Switch Toolchains {#switch-toolchains} We can switch toolchains globally by passing the `--toolchain=[name]` parameter to the `xmake f/config` command. :::tip NOTE This method is global. If we want to switch toolchains for a specific target, we need to use the [set\_toolchains](/api/description/project-target#set-toolchains) interface in the xmake.lua configuration. ::: If we want to switch it in the xmake.lua project configuration file, we can go to: [Configure the toolchain](/guide/project-configuration/toolchain-configuration) for further information. In addition, Xmake also provides some commonly used toolchains that can be switched directly, but the premise is that the user has installed the corresponding toolchain environment on the system. ## Gcc If the GCC toolchain is installed on Linux, xmake will usually detect and use it first. Of course, we can also manually switch to GCC to build. ```sh $ xmake f --toolchain=gcc -c $ xmake ``` ### Use the specified version of Gcc If the user additionally installs a specific version of the GCC toolchain such as gcc-11, gcc-10, the local gcc program may be named `/usr/bin/gcc-11`. One way is to switch by specifying the configuration one by one through `xmake f --cc=gcc-11 --cxx=gcc-11 --ld=g++-11`, but it is very cumbersome. Therefore, xmake also provides a faster switching method: ```sh $ xmake f --toolchain=gcc-11 -c $ xmake ``` You only need to specify the version name corresponding to `gcc-11` to quickly switch the entire GCC toolchain. ## Clang In macOS and Linux, usually xmake will try to automatically detect and use it first. Of course, we can also switch manually. ```sh $ xmake f --toolchain=clang -c $ xmake ``` On Windows, it will automatically load the MSVC environment. In addition, we also support PortableBuildTools + clang environment: ```sh $ xmake f -c --sdk=C:/BuildTools --toolchain=clang $ xmake -v [50%]: cache compiling.release src\main.cpp C:\Users\star\scoop\apps\llvm\current\bin\clang -c -Qunused-arguments -m64 --target=x86_64-windows-msvc -fexceptions -fcxx-exceptions -o build\.objs\test\windows\x64\release\src\main.cpp.obj src\main.cpp [75%]: linking.release test.exe C:\Users\star\scoop\apps\llvm\current\bin\clang++ -o build\windows\x64\release\test.exe build\.objs\test\windows\x64\release\src\main.cpp.obj -m64 --target=x86_64-windows-msvc [100%]: build ok, spent 0.235s ``` ## Clang-cl If you simply switch to the clang-cl.exe compiler, and use msvc for the rest of the link operation, then we don't need to switch the entire toolchain, just cut the c/c++ compiler. ```sh $ xmake f --cc=clang-cl --cxx=clang-cl -c $ xmake ``` Since xmake v2.7.2, there's also a dedicated clang-cl toolchain. The advantage of using the clang-cl toolchain over the msvc one is that on windows, the `--vs_toolset` option will be handled correctly. You can use it by running: ```sh $ xmake f --toolchain=clang-cl $ xmake ``` ## LLVM In addition to the independent clang compiler, if the user installs a complete llvm toolchain, we can also switch to it, including tools such as `llvm-ar`. ```sh $ xmake f --toolchain=llvm --sdk=/xxxx/llvm $ xmake ``` If it is a manually downloaded llvm sdk, we need to specify the llvm sdk root directory to ensure that xmake can find it. Of course, if the user has installed it in the PATH directory, the setting of the `--sdk` parameter is also optional. ## Cirle v2.5.9 xmake adds support for the circle compiler. This is a new C++20 compiler with some interesting compile-time meta-programming features. Those who are interested can check it out on the official website: https://www.circle-lang.org/ ```sh $ xmake f --toolchain=circle $ xmake ``` ## Tinyc [Tiny C Compiler](https://bellard.org/tcc/) is very lightweight. In some cases where you don't want to install heavy-weight compilers such as msvc/llvm, you may use it to quickly compile some c code. ```sh $ xmake f --toolchain=tinycc $ xmake ``` When using it, please add the tinycc compiler to the PATH environment. We can also use the remote toolchain to automatically download and integrate it, and truly achieve one-click compilation on all platforms without any manual installation operations by users. ```lua add_requires("tinycc") target("test") set_kind("binary") add_files("src/*.c) set_toolchains("@tinycc") ``` ## Armcc for Keil/MDK v2.5.9 added toolchain support for armcc under Keil/MDK, see related issue: [#1753](https://github.com/xmake-io/xmake/issues/1753) ```sh xmake f -p cross -a cortex-m3 --toolchain=armcc -c xmake ``` This toolchain is mainly used for embedded cross-compilation, so the `-p cross` cross-compilation platform is specified, and the cpu used by `-a cortex-m3` is specified, and the `-a/--arch` parameter is reused here. ## Armclang for Keil/MDK v2.5.9 adds toolchain support for armclang under Keil/MDK. For related issues, see: [#1753](https://github.com/xmake-io/xmake/issues/1753) ```sh xmake f -p cross -a cortex-m3 --toolchain=armclang -c xmake ``` This toolchain is mainly used for embedded cross-compilation, so the `-p cross` cross-compilation platform is specified, and the cpu used by `-a cortex-m3` is specified, and the `-a/--arch` parameter is reused here. ## GNU-RM Another cross toolchain for embedded arm, official website: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm ```sh $ xmake f --toolchain=gnu-rm -c $ xmake ``` ## SDCC It is also an embedded arm compilation toolchain. ```sh $ xmake f --toolchain=sdcc -a stm8 $ xmake ``` We can specify `-a stm8` to switch the cpu architecture, currently supported are: -stm8 -mcs51 -z80 -z180 -r2k -r3ka -s08 -hc08 ## Mingw The mingw toolchain is very commonly used and is available on all platforms. We can just switch the relevant toolchain: ```sh $ xmake f --toolchain=mingw -c $ xmake ``` However, in this way, the suffixes of some target files do not match exactly, so it is recommended to switch to the mingw platform for compilation and to support the download of dependent packages. ```sh $ xmake f -p mingw -c $ xmake ``` xmake will automatically detect the location of the mingw toolchain by default, macOS and msys/mingw64 environments can usually be automatically detected, if detected, you can also manually specify the mingw sdk path. ```sh $ xmake f -p mingw --mingw=/xxx/mingw -c $ xmake ``` Note that `--mingw` is used here instead of `--sdk`. In fact, both are ok, but the use of `--mingw` as a single parameter can better ensure that other cross-compilation toolchains do not conflict. ## LLVM-Mingw This is actually a project independent of Mingw, the usage is completely the same as Mingw, but it is based on LLVM, and provides arm/arm64 and other more architecture support, not just i386/x86\_64 ```sh $ xmake f -p mingw -a arm64 --mingw=/xxx/llvm-mingw -c $ xmake ``` If you want to use the arm/arm64 architecture of llvm-mingw, you need to specify the additional `-a arm64` parameter. In addition, the default xmake of llvm-mingw may not be able to detect it, and you need to set the sdk path additionally. ## Zig If you want to build a Zig program, we can automatically use the zig toolchain by executing xmake by default, but the premise is that zig is already in the PATH environment. ```sh $ xmake ``` Of course, we can also set it manually. ```sh $ xmake f --toolchain=zig -c $ xmake ``` You can also specify the path of the zig compiler. ```sh $ xmake f --toolchain=zig --zc=/xxxx/zig -c $ xmake ``` ### Zig CC We can also use the `zig cc` compiler provided by zig to compile C/C++ code. ```sh $ xmake f --cc="zig cc" --cxx="zig cc" --ld="zig c++" -c $ xmake ``` ### Cross compilation In addition, we can also use zig to achieve cross-compilation. ```sh $ xmake f -p cross --cross=riscv64-linux-musl --toolchain=zig $ xmake ``` Or compile the arm64 architecture: ```sh $ xmake f --toolchain=zig -a arm64 -c $ xmake ``` ## Emcc (WASM) If you want to compile the wasm program, we only need to switch to the wasm platform, and the emcc toolchain will be used to compile by default. ```sh $ xmake f -p wasm $ xmake ``` ## Wasi (WASM) This is another Wasm toolchain with WASI enabled, and we need to switch it manually. ```sh $ xmake f -p wasm --toolchain=wasi $ xmake ``` ## Icc (Intel C/C++ Compiler) We can also switch to Intel's C/C++ compiler to use. ```sh $ xmake f --toolchain=icc -c $ xmake ``` ## Ifort (Intel Fortain Compiler) We can also switch to Intel's Fortran compiler to use. ```sh $ xmake f --toolchain=ifort -c $ xmake ``` ## gfortran In addition to Intel's Fortran compiler, we also have the gnu fortran compiler available. ```sh $ xmake f --toolchain=gfortran -c $ xmake ``` ## fpc (Free Pascal) For pascal programs, xmake will use the fpc compiler to compile by default. ```sh $ xmake ``` Of course, we can also switch manually. ```sh $ xmake f --toolchain=fpc -c $ xmake ``` ## Dlang For dlang programs, xmake will use the dmd compiler to compile by default. ```sh $ xmake ``` Of course, we can also switch manually. ```sh $ xmake f --toolchain=dlang -c $ xmake ``` It should be noted that the dlang toolchain here actually includes automatic detection and switching of `dmd`, `ldc2` and `gdc`. ## Cuda For Cuda programs, we need to manually switch to the cuda toolchain. ```sh $ xmake f --toolchain=cuda -c $ xmake ``` We can also manually switch the C/C++ compiler called internally by nvcc. ```sh $ xmake f --toolchain=cuda --cu-ccbin=clang -c $ xmake ``` ## Assembler Regarding the independent assembler toolchain, xmake supports three: yasm, nasm, and fasm, which can be switched at will. If not set, the assembler that comes with gcc/clang/msvc will be used by default. ```sh $ xmake f --toolchain=nasm -c $ xmake ``` You can also specify the assembler path separately ```sh $ xmake f --toolchain=nasm --as=/xxx/nasm -c $ xmake ``` ## Go The golang compiler toolchain is automatically enabled when compiling go programs by default. ```sh $ xmake ``` ## Rust The rust compiler toolchain is automatically enabled when the rust program is compiled by default. ```sh $ xmake ``` At present, the rust toolchain can also support cross-compilation environments such as android. ```sh $ xmake f -p android --ndk=~/android-ndk-r20b -c $ xmake ``` ## NDK Android's NDK compilation toolchain, as long as the android platform is enabled, it will be enabled by default. ```sh $ xmake f -p android --ndk=~/android-ndk-r20b -c $ xmake ``` If the `--ndk` parameter is not specified, xmake will also detect it from the AndroidSDK/ndk-bundle directory and environment variables such as `$ANDROID_NDK_HOME`, `ANDROID_NDK_ROOT`, etc. by default. In addition, we can also set the global `xmake g --ndk=` configuration to avoid repeated settings each time. --- --- url: /guide/project-configuration/syntax-description.md --- # Syntax description Xmake's project description file `xmake.lua` is based on the Lua syntax, but in order to make the project build logic more convenient and concise, Xmake encapsulates it, making writing `xmake.lua` not as cumbersome as some makefiles. Basically, to write a simple project build description, just three lines, for example: ```lua target("test")     set_kind("binary")     add_files("src/*.c") ``` ## Configuration Separation Xmake.lua uses the 80:20 rule (aka [Pareto principle](https://en.wikipedia.org/wiki/Pareto_principle)) to implement a two-layer separate configuration of the description domain and the script domain. What is the 80:20 rule? In short, most of the project configuration, 80% of the cases, are basic configurations, such as: `add_cxflags`, `add_links`, etc. Only less than 20% of the space needs to be extra complex to meet some special configuration needs. The remaining 20% of the configuration is usually more complicated. If it is directly flooded in the whole `xmake.lua`, the whole project configuration will be very confusing and very unreadable. Therefore, Xmake isolates 80% of simple configuration and 20% of complex configuration by describing two different configurations: description domain and script domain, making the whole `xmake.lua` look very clear and intuitive, readable and maintainable. Get the best results. ### Description Scope For beginners who are just getting started, or just want to maintain some simple small projects, the requirements are fully met by describing the configuration completely. What is the description domain? It looks like this: ```lua target("test")     set_kind("binary")     add_files("src/*.c")     add_defines("DEBUG")     add_syslinks("pthread") ``` At first glance, it is actually a configuration set of `set_xxx`/`add_xxx`. For the novice, you can use it not as a Lua script, but just as an ordinary, but there are some basic rules configuration file. If, by looking, there are parentheses, or function calls like scripting languages, then we can also write this (whether with parentheses is up to personal preference): ```lua target "test"     set_kind "binary"     add_files "src/*.c"     add_defines "DEBUG"     add_syslinks "pthread" ``` Is this looking more like a profile? In fact, the description field is a configuration file, similar to the configuration of keys/values such as JSON, so even if you are not familiar with Lua, you can quickly get started. Moreover, for most projects, only the various settings of the project are configured by `set_xxx/add_xxx`, which has fully met the requirements. This is what I said at the beginning: 80% of the time, you can use the simplest configuration rules to simplify the configuration of the project, improve readability and maintainability, so that users and developers will be very friendly and more intuitive. What if we want to make some conditional judgments for different platforms and architectures? It doesn't matter, the description field, in addition to the basic configuration, also supports conditional judgment, as well as the for loop: ```lua target("test")     set_kind("binary")     add_files("src/*.c")     add_defines("DEBUG")     if is_plat("linux", "macosx") then         add_links("pthread", "m", "dl")     end ``` ```lua target("test")     set_kind("binary")     add_files("src/*.c")     add_defines("DEBUG")     for _, name in ipairs({"pthread", "m", "dl"}) do         add_links(name)     end ``` Is this looking a bit like Lua? Although, it can usually be regarded as a common configuration problem, but Xmake is based on Lua after all, so the description domain still supports the basic language features of Lua. However, it should be noted that although the description field supports Lua script syntax, try not to write too complicated Lua scripts in the description field, such as some time-consuming function calls and for loops. And in the description field, the main purpose is to set the configuration item, so Xmake does not completely open all module interfaces. Many interfaces are forbidden to be called in the description field. Even open callable interfaces are completely read-only, and time-consuming security interfaces such as `os.getenv()` read some general system information for configuration logic control. Also note that `xmake.lua` is parsed multiple times to resolve different configuration fields at different stages: for example: `option()`, `target()`, etc. So, don't think about writing complex Lua scripts in the description field of `xmake.lua`, and don't call print in the description field to display the information, because it will be executed multiple times, remember: it will be executed multiple times! ! ! ### Script Scope Restrict the description field to write complex Lua, all kinds of Lua modules and interfaces are not used? How to do? This time is the time for the script domain to appear. If the user is already fully familiar with Xmake's description domain configuration and feels that some of the special configuration maintenance on the project is not met, then we can do more complex configuration logic in the script domain: ```lua target("test")     set_kind("binary")     add_files("src/*.c")     on_load(function (target)         if is_plat("linux", "macosx") then             target:add("links", "pthread", "m", "dl")         end     end)     after_build(function (target)         import("core.project.config")         local targetfile = target:targetfile()         os.cp(targetfile, path.join(config.buildir(), path.filename(targetfile)))         print("build %s", targetfile)     end) ``` As long as it is similar: `on_xxx`, `after_xxx`, `before_xxx`, etc. The script inside the function body belongs to the script field. In the script domain, the user can do anything. Xmake provides an import interface to import various Lua modules built into Xmake, and can also import user-supplied Lua scripts. We can implement any function you want to implement in the script domain, even if you write a separate project. For some script fragments, if it is not very bloated, such as the above built-in writing is enough, if you need to implement more complex scripts, and do not want to be filled in a `xmake.lua`, you can separate the script into a separate Lua file for maintenance. E.g: ```lua target("test")     set_kind("binary")     add_files("src/*.c")     on_load("modules.test.load")     on_install("modules.test.install") ``` We can place the custom scripts in the corresponding directory of `xmake.lua`, and maintain them independently in `modules/test/load.lua` and `modules/test/install.lua`. In these independent Lua scripts, we can also import various built-in modules and custom modules through [import](/api/scripts/builtin-modules/import), just like writing Lua, Java is no different. For the different stages of the script's domain, `on_load` is mainly used for target loading, to do some dynamic configuration. Unlike the description field, it will only be executed once!!! In other stages, there are many, such as: `on/after/before`\_`build/install/package/run`, etc. See the target API manual section later, so I won't go into details here. ## Configuration Type In the description domain configuration, you can configure the configuration fields and configuration items. In the configuration domain, you can configure various configuration items through the interface of `set_xxx`/`add_xxx`. ```lua target("test1")     set_kind("binary")     add_files("src/*.c") target("test2")     set_kind("binary")     add_files("src/*.c") ``` In the above configuration, the target belongs to the configuration domain, and all the `set_xx`/`add_xxx` interface configurations below it belong to the configuration item, which is partially effective for this target. We can understand it as a local scope, similar to the block in C: ```c target("test1") {     set_kind("binary")     add_files("src/*.c") } target("test2") {     set_kind("binary")     add_files("src/*.c") } ``` However, in order to simplify the writing, Xmake stipulates that each newly defined target field starts, and the last configuration field ends automatically. Currently available configuration scopes are: `target()`, `option()`, `task()`, `package()` For a detailed description of each domain, see: [API Manual](/api/description/specification) ### Configuration Item As long as the configuration with the words `set_xxx` and `add_xxx` is a configuration item, multiple configuration items can be set in one configuration field. For a description of the configuration items, see: [Interface Specifications](/api/description/specification) ## Scope The description syntax of Xmake is divided by scope, which is mainly divided into: * external scope * Internal scope * Interface scope Which ones belong to the outside and which ones belong to the inside? if you look at the comments below, you know what it is: ```lua -- external scope target("test")     -- external scope     set_kind("binary")     add_files("src/*.c")     on_run(function ()         -- Internal scope         end)     after_package(function ()         -- Internal scope         end) -- external scope task("hello")     -- external scope     on_run(function ()         -- Internal scope         end) ``` Simply put, all within the custom script `function () end` belongs to the internal scope, which is the script scope, and all other places belong to the external scope. . ### External Scope For most projects, you don't need complicated engineering descriptions, and you don't need custom scripting support. You just need a simple `set_xxx` or `add_xxx` to meet your needs. Then according to the 28th law, 80% of the cases, we only need to write: ```lua target("test")     set_kind("static")     add_files("src/test/*.c") target("demo")     add_deps("test")     set_kind("binary")     add_links("test")     add_files("src/demo/*.c") ``` No complicated api calls, no complicated variable definitions, and if judgments and for loops. It's succinct and readable. At a glance, it doesn't matter if you don't understand lua grammar. As a simple description of the syntax, it looks a bit like a function call, you will know how to configure it at a basic point of programming. In order to be concise and secure, in this scope, many lua built-in apis are not open, especially related to writing files and modifying the operating environment, only providing some basic read-only interfaces, and logical operations. The current external scope lating lua built-in apis are: * table * string * pairs * ipairs * print * os Of course, although the built-in lua api does not provide much, Xmake also provides a lot of extension APIs. It is not much to describe the api. For details, please refer to: [API Manual](/api/scripts/builtin-modules/import) There are also some auxiliary apis, for example: * dirs: scan to get all the directories in the currently specified path * files: scan to get all the files in the current specified path * format: format string, short version of string.format There are also variable definitions and logical operations that can be used. after all, it is based on lua. The basic syntax is still there. We can switch the compiled files by if: ```lua target("test")     set_kind("static")     if is_plat("iphoneos") then         add_files("src/test/ios/*.c")     else         add_files("src/test/*.c")     end ``` It should be noted that the variable definition is divided into global variables and local variables. The local variables are only valid for the current `xmake.lua`, and do not affect the child `xmake.lua`. ```lua -- local variables, only valid for current xmake.lua local var1 = 0 -- global variables that affect all subsmake.lua included after includes() var2 = 1 includes("src") ``` ### Internal Scope Also known as plug-ins, script scope, provide more complex and flexible script support, generally used to write some custom scripts, plug-in development, custom task tasks, custom modules, etc. Usually included by `function () end`, and passed to the `on_xxx`, `before_xxx` and `after_xxx` interfaces, are all self-scoped. E.g: ```lua -- custom script target("hello")     after_build(function ()         -- Internal scope         end) -- custom tasks, plugins task("hello")     on_run(function ()         -- Internal scope         end) ``` In this scope, not only can you use most lua apis, but you can also use many extension modules provided by Xmake. All extension modules are imported through import. For details, please refer to: [import module document](/api/scripts/builtin-modules/import) Here we give a simple example, after the compilation is complete, ldid signature on the ios target program: ```lua target("iosdemo")     set_kind("binary")     add_files("*.m")     after_build(function (target)         -- Execute signature, if it fails, automatically interrupt, giving a highlight error message         os.run("ldid -S$(projectdir)/entitlements.plist %s", target:targetfile())     end) ``` It should be noted that in the internal scope, all calls are enabled with the exception catching mechanism. if the operation is wrong, Xmake will be automatically interrupted and an error message will be given. Therefore, the script is written without the cumbersome `if retval then` judgment, and the script logic is more clear. ### Interface Scope All descriptions of api settings in the external scope are also scoped. They are called in different places and have different scopes of influence, for example: ```lua -- global root scope, affecting all targets, including subproject target settings in includes() add_defines("DEBUG") -- define or enter the demo target scope (support multiple entry to append settings) target("demo")     set_kind("shared")     add_files("src/*.c")     -- the current target scope only affects the current target     add_defines("DEBUG2") -- option settings, only local settings are supported, not affected by global api settings option("test")     -- local scope of the current option     set_default(false) -- other target settings, -DDEBUG will also be set target("demo2")     set_kind("binary")     add_files("src/*.c") -- re-enter the demo target scope target("demo")     -- append macro definitions, only valid for the current demo target     add_defines("DEBUG3") ``` Normally, entering another target/option domain setting will automatically leave the previous target/option field, but sometimes in order to compare some scope pollution, we can show off a domain, for example: ```lua option("test")     set_default(false) option_end() target("demo")     set_kind("binary")     add_files("src/*.c") target_end() ``` Call `option_end()`, `target_end()` to explicitly leave the current target/option field setting. ### Scope Indentation Indentation in xmake.lua is just a specification for more clear distinction. The current setting is for that scope, although it is ok even if it is not indented, but it is not very readable. . e.g: ```lua target("xxxx")     set_kind("binary")     add_files("*.c") ``` with ```lua target("xxxx") set_kind("binary") add_files("*.c") ``` The above two methods are the same in effect, but in understanding, the first one is more intuitive. At first glance, you know that `add_files` is only set for target, not global. Therefore, proper indentation helps to better maintain `xmake.lua`. Finally attached, tbox's [xmake.lua](https://github.com/tboox/tbox/blob/master/src/tbox/xmake.lua) description, for reference only. . ### Code formatting The default indentation of the description field configuration syntax does not conform to the lua formatting specification, so the lua language server does not support formatting it. If you want the IDE, the editor, to support formatting indentation of the configuration better, you can do so by writing `do end` as follows ```lua target("bar") do set_kind("binary") add_files("src/*.cpp") end target("foo") do set_kind("binary") add_files("src/*.cpp") end ``` This allows the Lua LSP to format it correctly as standard lua code, whether this is required or not depends on the user's needs. If you don't have a habit of using automatic code formatting, then you don't need to do this. ## Syntax simplification The configuration field syntax of `xmake.lua` is very flexible and can be used in a variety of complex and flexible configurations in the relevant domain, but for many streamlined small block configurations, this time is slightly redundant: ```lua option("test1")     set_default(true)     set_showmenu(true)     set_description("test1 option") option("test2")     set_default(true)     set_showmeu(true) option("test3")     set_default("hello") ``` Xmake 2.2.6 or later, for the above small block option domain settings, we can simplify the description into a single line: ```lua option("test1", {default = true, showmenu = true, description = "test1 option"}) option("test2", {default = true, showmenu = true}) option("test3", {default = "hello"}) ``` In addition to the option field, this simplified writing is also supported for other domains, such as: ```lua target("demo")     set_kind("binary")     add_files("src/*.c") ``` Simplified to: ```lua target("demo", {kind = "binary", files = "src/*.c"}) ``` or ```lua target("demo", { kind = "binary", files = "src/*.c" }) ``` Of course, if the configuration requirements are more complicated, or the original multi-line setting method is more convenient, this depends on your own needs to evaluate which method is used. ## Optional Scope Configuration Syntax Our default convention for domain configuration syntax, although very concise, is not very friendly to auto-formatted indentation and IDEs. ```lua target("foo") set_kind("binary") add_files("src/*.cpp") target_end() ``` Also, it does not automatically end the current target scope, the user needs to explicitly call `target_end()`. Although, as we mentioned above, the `do end` pattern can be used to solve the auto-indentation problem, the problem of needing `target_end()` still exists. ```lua target("bar") do set_kind("binary") add_files("src/*.cpp") end target_end() ``` In version 2.7.3, we provide a better optional domain configuration syntax to solve the auto-indent, target domain isolation problem, e.g. ```lua add_defines("ROOT") target("foo", function () set_kind("binary") add_files("src/*.cpp") add_defines("FOO") end) target("bar", function () set_kind("binary") add_files("src/*.cpp") add_defines("BAR") end) ``` The foo and bar domains are completely isolated and we can configure other settings between them without affecting them, plus it is very LSP friendly. --- --- url: /api/scripts/builtin-modules/table.md --- # table Table belongs to the module provided by Lua native. For the native interface, you can refer to: [lua official document](https://www.lua.org/manual/5.1/manual.html#5.5) It has been extended in xmake to add some extension interfaces: ## table.join * Merge multiple tables and return #### Function Prototype ::: tip API ```lua table.join(tables:
, ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tables | Table to merge | | ... | Variable arguments, can pass multiple tables | #### Usage You can merge the elements in multiple tables and return to a new table, for example: ```lua local newtable = table.join({1, 2, 3}, {4, 5, 6}, {7, 8, 9}) ``` The result is: `{1, 2, 3, 4, 5, 6, 7, 8, 9}` And it also supports the merging of dictionaries: ```lua local newtable = table.join({a = "a", b = "b"}, {c = "c"}, {d = "d"}) ``` The result is: `{a = "a", b = "b", c = "c", d = "d"}` ## table.join2 * Combine multiple tables into the first table #### Function Prototype ::: tip API ```lua table.join2(target:
, tables:
, ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | target | Target table to merge into | | tables | Table to merge | | ... | Variable arguments, can pass multiple tables | #### Usage Similar to [table.join](#table-join), the only difference is that the result of the merge is placed in the first argument, for example: ```lua local t = {0, 9} table.join2(t, {1, 2, 3}) ``` The result is: `t = {0, 9, 1, 2, 3}` ## table.unique * Deduplicate the contents of the table #### Function Prototype ::: tip API ```lua table.unique(tbl:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tbl | Table to deduplicate | #### Usage To de-table elements, generally used in array tables, for example: ```lua local newtable = table.unique({1, 1, 2, 3, 4, 4, 5}) ``` The result is: `{1, 2, 3, 4, 5}` ## table.slice * Get the slice of the table #### Function Prototype ::: tip API ```lua table.slice(tbl:
, start: , stop: , step: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tbl | Table to slice | | start | Start index | | stop | Stop index (optional) | | step | Step size (optional) | #### Usage Used to extract some elements of an array table, for example: ```lua -- Extract all elements after the 4th element, resulting in: {4, 5, 6, 7, 8, 9} table.slice({1, 2, 3, 4, 5, 6, 7, 8, 9}, 4) -- Extract the 4th-8th element and the result: {4, 5, 6, 7, 8} table.slice({1, 2, 3, 4, 5, 6, 7, 8, 9}, 4, 8) -- Extract the 4th-8th element with an interval of 2, resulting in: {4, 6, 8} table.slice({1, 2, 3, 4, 5, 6, 7, 8, 9}, 4, 8, 2) ``` ## table.contains * Determine that the table contains the specified value #### Function Prototype ::: tip API ```lua table.contains(tbl:
, values: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tbl | Table to check | | values | Values to check for | | ... | Variable arguments, can pass multiple values | #### Usage ```lua if table.contains(t, 1, 2, 3) then - ... end ``` As long as the table contains any value from 1, 2, 3, it returns true ## table.orderkeys * Get an ordered list of keys #### Function Prototype ::: tip API ```lua table.orderkeys(tbl:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | tbl | Table to get keys from | #### Usage The order of the key list returned by `table.keys(t)` is random. If you want to get an ordered key list, you can use this interface. --- --- url: /zh/api/scripts/builtin-modules/table.md --- # table table 属于 lua 原生提供的模块,对于原生接口使用可以参考:[lua官方文档](https://www.lua.org/manual/5.1/manual.html#5.5) Xmake 中对其进行了扩展,增加了一些扩展接口: ## table.join * 合并多个table并返回 #### 函数原型 ::: tip API ```lua table.join(tables:
, ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tables | 要合并的表格 | | ... | 可变参数,可以传递多个表格 | #### 用法说明 可以将多个table里面的元素进行合并后,返回到一个新的table中,例如: ```lua local newtable = table.join({1, 2, 3}, {4, 5, 6}, {7, 8, 9}) ``` 结果为:`{1, 2, 3, 4, 5, 6, 7, 8, 9}` 并且它也支持字典的合并: ```lua local newtable = table.join({a = "a", b = "b"}, {c = "c"}, {d = "d"}) ``` 结果为:`{a = "a", b = "b", c = "c", d = "d"}` ## table.join2 * 合并多个table到第一个table #### 函数原型 ::: tip API ```lua table.join2(target:
, tables:
, ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | target | 目标表格 | | tables | 要合并的表格 | | ... | 可变参数,可以传递多个表格 | #### 用法说明 类似[table.join](#table-join),唯一的区别是,合并的结果放置在第一个参数中,例如: ```lua local t = {0, 9} table.join2(t, {1, 2, 3}) ``` 结果为:`t = {0, 9, 1, 2, 3}` ## table.unique * 对table中的内容进行去重 #### 函数原型 ::: tip API ```lua table.unique(tbl:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tbl | 要去重的表格 | #### 用法说明 去重table的元素,一般用于数组table,例如: ```lua local newtable = table.unique({1, 1, 2, 3, 4, 4, 5}) ``` 结果为:`{1, 2, 3, 4, 5}` ## table.slice * 获取table的切片 #### 函数原型 ::: tip API ```lua table.slice(tbl:
, start: , stop: , step: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tbl | 要切片的表格 | | start | 开始索引 | | stop | 结束索引(可选) | | step | 步长(可选) | #### 用法说明 用于提取数组table的部分元素,例如: ```lua -- 提取第4个元素后面的所有元素,结果:{4, 5, 6, 7, 8, 9} table.slice({1, 2, 3, 4, 5, 6, 7, 8, 9}, 4) -- 提取第4-8个元素,结果:{4, 5, 6, 7, 8} table.slice({1, 2, 3, 4, 5, 6, 7, 8, 9}, 4, 8) -- 提取第4-8个元素,间隔步长为2,结果:{4, 6, 8} table.slice({1, 2, 3, 4, 5, 6, 7, 8, 9}, 4, 8, 2) ``` ## table.contains * 判断 table 中包含指定的值 #### 函数原型 ::: tip API ```lua table.contains(tbl:
, values: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tbl | 要检查的表格 | | values | 要检查的值 | | ... | 可变参数,可以传递多个值 | #### 用法说明 ```lua if table.contains(t, 1, 2, 3) then -- ... end ``` 只要 table 中包含 1, 2, 3 里面任意一个值,则返回 true then ## table.orderkeys * 获取有序的 key 列表 #### 函数原型 ::: tip API ```lua table.orderkeys(tbl:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tbl | 要获取键的表格 | #### 用法说明 `table.keys(t)` 返回的 key 列表顺序是随机的,想要获取有序 key 列表,可以用这个接口。 --- --- url: /api/scripts/target-instance.md --- # Target Instance This page describes the interface for `target` of functions like `on_load()`, `before_build()` or `after_install()` of the [Project target](/api/description/project-target). ## target:name * Get the name of the target #### Function Prototype ::: tip API ```lua target:name() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ## target:get * Get the values of the target by name #### Function Prototype ::: tip API ```lua target:get(key: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | #### Usage ```lua -- get the links target:get("links") -- get the defined macros target:get("defines") ``` ## target:set * Set the values of the target by name #### Function Prototype ::: tip API ```lua target:set(key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | | value | Configuration value | #### Usage If you just want to add values use [target:add](#target-add). ```lua -- set the links target:set("links", "sdl2") -- set the defined macros target:set("defines", "SDL_MAIN_HANDLED") ``` ::: tip NOTE Any script scope configuration using `target:set("xxx", ...)` is completely consistent with the corresponding `set_xxx` interface in the description scope. For specific parameter descriptions, you can directly refer to the corresponding `set_xxx` interface documentation in the description scope. For example: * Description scope: `set_kind("binary")` * Script scope: `target:set("kind", "binary")` ::: ## target:add * Add to the values of the target by name #### Function Prototype ::: tip API ```lua target:add(key: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | key | Configuration key name | | value | Value to add | #### Usage ```lua -- add links target:add("links", "sdl2") -- add defined macros target:add("defines", "SDL_MAIN_HANDLED") ``` ::: tip NOTE Any script scope configuration using `target:add("xxx", ...)` is completely consistent with the corresponding `add_xxx` interface in the description scope. For specific parameter descriptions, you can directly refer to the corresponding `add_xxx` interface documentation in the description scope. For example: * Description scope: `add_files("src/*.c", {defines = "PRIVATE"})` * Script scope: `target:add("files", "src/*.c", {defines = "PRIVATE"})` ::: ## target:kind * Get the target program type #### Function Prototype ::: tip API ```lua target:kind() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Corresponding to `set_kind` description domain interface settings. The main target types are: binary, static, shared, phony, object, headeronly. ## target:is\_plat * Whether the current platform is one of the given platforms #### Function Prototype ::: tip API ```lua target:is_plat(plat: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | plat | Platform name | #### Usage Although we can also use the `is_plat` global interface to directly determine the platform, xmake supports the use of `set_plat` to set the compilation platform separately for a specific target. At this time, using the global interface is not applicable, so we usually recommend using the interface provided by the target to directly determine the compilation platform for the current target, which is more reliable. ```lua - Is the current platform android? target:is_plat("android") - Is the current platform windows, linux or macosx? target:is_plat("windows", "linux", "macosx") ``` ## target:is\_arch * Is the current architecture one of the given architectures #### Function Prototype ::: tip API ```lua target:is_arch(arch: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | arch | Architecture name | #### Usage Although we can also use the `is_arch` global interface to directly determine the architecture, xmake supports the use of `set_arch` to set the compilation architecture separately for a specific target. At this time, using the global interface is not applicable, so we usually recommend using the interface provided by the target to directly judge the compilation architecture of the current target, which is more reliable. ```lua - Is the current architecture x86 target:is_arch("x86") - Is the current architecture x64 or x86_64 target:is_arch("x64", "x86_64") ``` ## target:is\_arch64 * Is the current architecture a 64-bit one #### Function Prototype ::: tip API ```lua target:is_arch64() ``` ::: #### Parameter Description No parameters required for this function. #### Usage ```lua - Is the current architecture 64-bit? target:is_arch64() ``` ## target:targetfile * Get the target file path #### Function Prototype ::: tip API ```lua target:targetfile() ``` ::: #### Parameter Description No parameters required for this function. #### Usage It is mainly used to obtain the output path of static, shared, and binary object program files. ```lua os.cp(target:targetfile(), "/tmp/") ``` ## target:artifactfile * Get the artifact file of the target #### Function Prototype ::: tip API ```lua target:artifactfile(kind: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | kind | Target kind | #### Usage Currently, only the implib file output path of Windows DLL can be obtained. ```lua target:artifactfile("implib") ``` However, it may be extended to other types of artifact file path acquisition in the future. ## target:targetdir * Get the output directory of the target file #### Function Prototype ::: tip API ```lua target:targetdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage That is, the storage directory corresponding to target:targetfile(). ## target:basename * Get the base name of the target file #### Function Prototype ::: tip API ```lua target:basename() ``` ::: #### Parameter Description No parameters required for this function. #### Usage That is, `foo` in libfoo.a, foo.dll, foo.exe. ## target:filename * Get the target file name #### Function Prototype ::: tip API ```lua target:filename() ``` ::: #### Parameter Description No parameters required for this function. #### Usage The full file name of the target file, equivalent to `path.filename(target:targetfile())`. ## target:installdir * Get the installation directory of the target file #### Function Prototype ::: tip API ```lua target:installdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage It is usually used to obtain the corresponding installation directory path in scripts such as after\_install of `xmake install/uninstall`, which can be used for user-defined installation scripts. ## target:autogendir * Get auto-generated catalog #### Function Prototype ::: tip API ```lua target:autogendir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage This is usually used in some custom rule scripts to store some target-specific automatically generated files, and the path is usually under `build/.gens/target`. For example, when we are processing lex/yacc, some source code files are automatically generated, and they can be stored in this directory so that they can be processed later. ## target:objectfile * Get the object file path #### Function Prototype ::: tip API ```lua target:objectfile(sourcefile: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | sourcefile | Source file path | #### Usage Usually used in custom scripts to obtain the target file path corresponding to the source file, for example ```lua local objectfile = target:objectfile(sourcefile) ``` ## target:sourcebatches * Get all source files #### Function Prototype ::: tip API ```lua target:sourcebatches() ``` ::: #### Parameter Description No parameters required for this function. #### Usage It can get all the source files added by `add_files` and store them separately according to different source file types. The approximate structure is as follows: ```lua { "c++.build" = { objectfiles = { "build/.objs/test/macosx/x86_64/release/src/main.cpp.o" }, rulename = "c++.build", sourcekind = "cxx", sourcefiles = { "src/main.cpp" }, dependfiles = { "build/.deps/test/macosx/x86_64/release/src/main.cpp.o.d" } }, "asm.build" = { objectfiles = { "build/.objs/test/macosx/x86_64/release/src/test.S.o" }, rulename = "asm.build", sourcekind = "as", sourcefiles = { "src/test.S" }, dependfiles = { "build/.deps/test/macosx/x86_64/release/src/test.S.o.d" } } } ``` We can traverse to obtain and process each type of source file. ```lua for _, sourcebatch in pairs(target:sourcebatches()) do local sourcekind = sourcebatch.sourcekind if sourcekind == "cc" or sourcekind == "cxx" or sourcekind == "as" then for _, sourcefile in ipairs(sourcebatch.sourcefiles) do - TODO end end end ``` Where sourcekind is the type of each source file, cc is the c file type, cxx is the c++ source file, and as is the asm source file. sourcebatch corresponds to each type of source file batch, corresponding to a batch of source files of the same type. sourcebatch.sourcefiles is a list of source files, sourcebatch.objectfiles is a list of object files, and sourcebatch.rulename is the name of the corresponding rule. ## target:objectfiles * Get a list of all object files #### Function Prototype ::: tip API ```lua target:objectfiles() ``` ::: #### Parameter Description No parameters required for this function. #### Usage Although `target:sourcebatches()` can also obtain all object files, they are classified according to the source file type and do not directly participate in the final link. If we want to dynamically modify the final linked object file list, we can modify `target:objectfiles()`, which is an array list. ## target:headerfiles * Get a list of all header files #### Function Prototype ::: tip API ```lua target:headerfiles() ``` ::: #### Parameter Description No parameters required for this function. #### Usage You can get a list of all header files set by the `add_headerfiles()` interface. ```lua for _, headerfile in ipairs(target:headerfiles()) do - TODO end ``` ## target:scriptdir * Get the xmake.lua directory where the target definition is located #### Function Prototype ::: tip API ```lua target:scriptdir() ``` ::: #### Parameter Description No parameters required for this function. #### Usage This is usually used in custom rules. If you want to get the directory where the current target is actually defined in xmake.lua, it is convenient to reference some resource files. You can use this interface. ## target:has\_cxxfuncs * Check whether the target compilation configuration can obtain the given C++ function #### Function Prototype ::: tip API ```lua target:has_cxxfuncs(funcs: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | funcs | Function name or function name list | #### Usage The usage is similar to [target:has\_cfuncs](#target-has_cfuncs), except that it is mainly used to detect C++ functions. However, while detecting functions, we can also additionally configure std languages to assist detection. ``` target:has_cxxfuncs("foo", {includes = "foo.h", configs = {languages = "cxx17"}}) ``` ## target:has\_ctypes * Check whether the target compilation configuration can obtain the given C type #### Function Prototype ::: tip API ```lua target:has_ctypes(types: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | Type name or type name list | #### Usage This should be used in `on_config` like this: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function(target) if target:has_ctypes("z_stream", {includes = "zlib.h"}) then target:add("defines", "HAVE_ZSTEAM_T") end end) ``` ## target:has\_cxxtypes * Check whether the target compilation configuration can get the given C++ type #### Function Prototype ::: tip API ```lua target:has_cxxtypes(types: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | Type name or type name list | #### Usage The usage is similar to [target:has\_ctypes](#target-has_ctypes), except that it is mainly used to detect the type of C++. ## target:has\_cflags * Check whether the target compilation configuration can obtain the given C compilation flags #### Function Prototype ::: tip API ```lua target:has_cflags(flags: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | flags | Compilation flags or flag list | #### Usage ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) if target:has_cxxflags("-fPIC") then target:add("defines", "HAS_PIC") end end) ``` ## target:has\_cxxflags * Check whether the target compilation configuration can obtain the given C++ compilation flags #### Function Prototype ::: tip API ```lua target:has_cxxflags(flags: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | flags | Compilation flags or flag list | #### Usage The usage is similar to [target:has\_cflags](#target-has_cflags), except that it is mainly used to detect the compilation flags of C++. ## target:has\_cincludes * Check whether the target compilation configuration can obtain the given C header file #### Function Prototype ::: tip API ```lua target:has_cincludes(includes: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Include file or include file list | #### Usage This should be used in `on_config`, for example, it can be used to determine whether the current target can obtain the zlib.h header file of the zlib dependency package, and then automatically define `HAVE_INFLATE`: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function(target) if target:has_cincludes("zlib.h") then target:add("defines", "HAVE_ZLIB_H") end end) ``` ## target:has\_cxxincludes * Check whether the target compilation configuration can obtain the given C++ header file #### Function Prototype ::: tip API ```lua target:has_cxxincludes(includes: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includes | Include file or include file list | #### Usage The usage is similar to [target:has\_cincludes](#target-has_cincludes), except that it is mainly used to detect C++ header files. ## target:check\_csnippets * Detect whether a given piece of C code can be compiled and linked #### Function Prototype ::: tip API ```lua target:check_csnippets(snippets: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | snippets | Code snippet or code snippet list | #### Usage The usage is similar to [target:check\_cxxsnippets](#target-check_cxxsnippets), except that it is mainly used to detect C code snippets. ## target:check\_cxxsnippets * Detect if a given piece of C++ code can be compiled and linked #### Function Prototype ::: tip API ```lua target:check_cxxsnippets(snippets: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | snippets | Code snippet or code snippet list | #### Usage This should be used in `on_config` like this: ```lua add_requires("libtins") target("test") set_kind("binary") add_files("src/*.cpp") add_packages("libtins") on_config(function(target) local has_snippet = target:check_cxxsnippets({test = [[ #include using namespace Tins; void test() { std::string name = NetworkInterface::default_interface().name(); printf("%s\n", name.c_str()); } ]]}, {configs = {languages = "c++11"}, includes = {"tins/tins.h"}})) if has_snippet then target:add("defines", "HAS_XXX") end end) ``` By default, it only checks whether the compilation link is passed. If you want to try the runtime check, you can set `tryrun = true`. ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) local has_int_4 = target:check_cxxsnippets({test = [[ return (sizeof(int) == 4)? 0 : -1; ]]}, {configs = {languages = "c++11"}, tryrun = true})) if has_int_4 then target:add("defines", "HAS_INT4") end end) ``` We can also continue to capture the running output of the detection by setting `output = true`, and add a custom `main` entry to achieve a complete test code, not just a code snippet. ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) local int_size = target:check_cxxsnippets({test = [[ #include int main(int argc, char** argv) { printf("%d", sizeof(int)); return 0; return 0; } ]]}, {configs = {languages = "c++11"}, tryrun = true, output = true})) end) ``` ## target:check\_sizeof * Detect type size #### Function Prototype ::: tip API ```lua target:check_sizeof(types: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | types | Type name or type name list | #### Usage ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) print("sizeof(long) = %s", target:check_sizeof("long")) print("sizeof(string) = %s", target:check_sizeof("std::string", {includes = "string"})) if target:check_size("long") == 8 then target:add("defines", "LONG64") end end) ``` ```sh $ xmake sizeof(long) = 8 sizeof(string) = 24 ``` ## target:has\_features * Detect if specified C/C++ compiler feature #### Function Prototype ::: tip API ```lua target:has_features(features: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | features | Feature name or feature name list | #### Usage It is faster than using `check_cxxsnippets`, because it only performs preprocessing once to check all compiler features, instead of calling the compiler every time to try to compile. ``` target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) if target:has_features("c_static_assert") then target:add("defines", "HAS_STATIC_ASSERT") end if target:has_features("cxx_constexpr") then target:add("defines", "HAS_CXX_CONSTEXPR") end end) ``` --- --- url: /guide/extensions/theme-style.md --- # Theme Style ## Switch Theme If users don't like xmake's default display color and style, we can use the following global configuration commands to switch to other configuration themes provided by xmake, for example: ```sh $ xmake g --theme=dark ``` The default theme is named default. Here, we switch to the dark style theme to adapt to some scenes with a very light background and provide darker color output to avoid being unclear. If we want to switch back to the default theme, we can directly type: ```sh $ xmake g -c ``` or ```sh $ xmake g --theme=default ``` In addition, xmake also provides a lot of interesting and practical built-in themes, everyone can try, the following will explain in detail. ::: tip NOTE If you have a better theme, you are welcome to submit a PR contribution. Thank you very much! ::: ## Builtin Themes ### Default theme This is the default display theme after we install xmake. Theme name: default, which will provide color output by default, suitable for some dark background terminals. We can also switch back to the default theme with the following command: ```sh $ xmake g --theme=default ``` ### Ninja Theme This is the theme provided by the version after v2.3.4. The build progress style is similar to ninja. It uses a single-line progress bar, and the progress is no longer rolled back. The configuration of the default theme is the same except that the progress is displayed differently. ```sh $ xmake g --theme=ninja ``` ### Emoji Theme This theme part output uses emoji characters instead of the previous color output. ```sh $ xmake g --theme=emoji ``` ### Dark Theme This theme is mainly for some terminal backgrounds with a light color system (such as light yellow, etc.), which causes some warning outputs (the default is also yellow) to be invisible, so the theme color is changed to a dark system to improve visibility. ```sh $ xmake g --theme=dark ``` ### Light theme This theme is mainly for the dark background of some terminals, which makes some outputs overlap and become invisible, so the theme color is changed to a light color to improve visibility. ```sh $ xmake g --theme=light ``` ### Plain Theme In fact, this theme is to completely disable color and emoji output, mainly to deal with the problem of garbled display caused by some terminals that do not support color codes, and it is also the simplest theme style. ::: tip NOTE Some Windows terminals may not support colors. You can set this theme to solve the problem of garbled display. ::: ```sh $ xmake g --theme=plain ``` ### Powershell theme The background of the PowerShell terminal under Windows is blue, and its palette configuration seems to be changed. The magenta color is actually displayed as the background blue, which is very strange, resulting in the local output of xmake's default output being invisible (overlapped). Therefore, this theme is to better adapt the display output under the PowerShell terminal. ```sh $ xmake g --theme=powershell ``` --- --- url: /guide/project-configuration/toolchain-configuration.md --- # Toolchain Configuration {#toolchain-configuration} ## Switch toolchains {#switch-toolchains} Previously, we mentioned that we can use the command line `xmake f --toolchain=[name]` to switch toolchains globally. For more information, see: [Command line toolchain switching](/guide/basic-commands/switch-toolchains). Although switching in the command line is fast and convenient, it can only switch globally. If there are multiple targets in the project, and we only want to switch the toolchain for one of them, we can use [set\_toolchains](/api/description/project-target.html#set-toolchains) in the configuration file to configure it. For example: ```lua target("test") set_kind("binary") add_files("src/*.c") set_toolchains("clang", "yasm") ``` Of course, if we place set\_toolchains in the top-level root scope of the configuration file, it will take effect on all targets and can also play a global configuration switch effect. ```lua set_toolchains("clang", "yasm") target("foo") set_kind("binary") add_files("src/*.c") target("bar") set_kind("binary") add_files("src/*.c") ``` For more description of this interface, please go to the [set\_toolchains](/api/description/project-target#set-toolchains) API manual page for details. ## Switch toolset {#switch-toolsets} In addition to switching toolchains through set\_toolchains, we can also use the set\_toolset interface to switch a compiler locally for a target. The difference between it and set\_toolchains is that toolchain is a collection of all series of tools including compiler, linker, library archiver and assembler, while toolset only switches a compilation tool within a toolchain. ```lua target("test1") add_files("*.c") target("test2") add_files("*.c") set_toolset("cc", "$(projectdir)/tools/bin/clang-5.0") ``` :::tip NOTE Its granularity is smaller, but unless necessary, we still recommend users to use set\_toolchains to achieve unified switching. ::: For more information about this interface, please visit the [set\_toolset](/api/description/project-target#set-set_toolset) API manual page. ## Custom toolchain {#custom-toolchains} We can also implement custom toolchains through the toolchain interface, for example: ```lua toolchain("my_muslcc") set_homepage("https://musl.cc/") set_description("The musl-based cross-compilation toolchains") set_kind("cross") on_load(function (toolchain) toolchain:load_cross_toolchain() if toolchain:is_arch("arm") then toolchain:add("cxflags", "-march=armv7-a", "-msoft-float", {force = true}) toolchain:add("ldflags", "-march=armv7-a", "-msoft-float", {force = true}) end toolchain:add("syslinks", "gcc", "c") end) toolchain_end() target("test") set_kind("binary") add_files("src/*.c") set_toolchains("my_muslcc") ``` Here, we customize a my\_muslcc cross-compilation toolchain and set it to the test target. For more information about custom toolchains, you can go to the [Custom Toolchain](/api/description/custom-toolchain) manual. ## Automatically pull remote toolchains {#pull-remote-toolchains} Starting from version 2.5.2, we can pull the specified toolchain to integrate the compilation project. We also support switching the dependent package to the corresponding remote toolchain to participate in the compilation and then integrate it. For relevant example codes, see: [Toolchain/Packages Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/package) Related issue [#1217](https://github.com/xmake-io/xmake/issues/1217) ### Pull the specified version of the llvm toolchain We use clang in llvm-10 to compile the project. ```lua add_requires("llvm 10.x", {alias = "llvm-10"}) target("test") set_kind("binary") add_files("src/*.c) set_toolchains("llvm@llvm-10") ``` ### Pull cross-compilation toolchain {#pull-and-bind-cross-toolchain} We can also pull the specified cross-compilation toolchain to compile the project. ```lua add_requires("muslcc") target("test") set_kind("binary") add_files("src/*.c) set_toolchains("@muslcc") ``` ### Pull toolchain and integrate the corresponding toolchain compiled dependency packages We can also use the specified muslcc cross-compilation toolchain to compile and integrate all dependency packages. ```lua add_requires("muslcc") add_requires("zlib", "libogg", {system = false}) set_toolchains("@muslcc") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libogg") ``` For complete examples, see: [Examples (muslcc)](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/toolchain_muslcc/xmake.lua) ### Pull integrated Zig toolchain ```lua add_rules("mode.debug", "mode.release") add_requires("zig 0.7.x") target("test") set_kind("binary") add_files("src/*.zig") set_toolchains("@zig") ``` ### Pull and bind a custom toolchain We can also customize a toolchain `my_muslcc` and bind it to the corresponding toolchain package `muslcc`. ```lua toolchain("my_muslcc") set_homepage("https://musl.cc/") set_description("The musl-based cross-compilation toolchains") set_kind("cross") on_load(function (toolchain) toolchain:load_cross_toolchain() if toolchain:is_arch("arm") then toolchain:add("cxflags", "-march=armv7-a", "-msoft-float", {force = true}) toolchain:add("ldflags", "-march=armv7-a", "-msoft-float", {force = true}) end toolchain:add("syslinks", "gcc", "c") end) toolchain_end() add_requires("muslcc") target("test") set_kind("binary") add_files("src/*.c") set_toolchains("my_muslcc@muslcc") ``` :::tip NOTE In addition to configuring `set_toolchains("my_muslcc@muslcc")`, the binding logic of the toolchain and the package also requires you to search for the toolchain package in the toolchain to take effect. ::: For example, in the on\_load of the cross-compilation toolchain, you need to configure the following code to find the corresponding toolchain from the installation directory of the bound toolchain package `muslccc`. ```lua for _, package in ipairs(toolchain:packages()) do local installdir = package:installdir() if installdir and os.isdir(installdir) then cross_toolchain = find_cross_toolchain(installdir, {cross = cross}) if cross_toolchain then break end end end ``` Of course, if we customize the cross-compilation toolchain, we can also simply call `toolchain:load_cross_toolchain()` to achieve the same effect, which has encapsulated the above search logic. ```lua on_load(function (toolchain) toolchain:load_cross_toolchain() end) ``` For more details, you can refer to the complete example: [Binding remote custom toolchain](https://github.com/xmake-io/xmake/blob/dev/tests/projects/package/toolchain_muslcc/xmake.lua) ## Customize unknown compilation toolchain The custom toolchains we talked about before are all for some known compilers, such as gcc, clang, msvc, etc. Xmake has adapted them internally and knows how to configure compilation parameters and how to execute compilation commands. If it is a completely unknown compiler, its compilation parameters and usage are very different from gcc and clang, Xmake cannot know how to call them to compile. Therefore, we need to further customize them, including mapping compilation parameters, compilation parameter detection, compilation command generation and execution, etc. We provide a complete example, which can be directly referenced: [Complete Custom Unknown Toolchain](https://github.com/xmake-io/xmake/tree/dev/tests/apis/custom_toolchain). ::: tip NOTE If the compiler's compilation parameters and usage are completely similar to gcc/clang, it is a derivative compiler of the same type as gcc/clang, and only the tool name is different. We do not need to completely customize them. For example, if the mycompiler.exe compiler is similar to the usage of gcc, then you only need to configure `set_toolset("gcc@mycompiler.exe")` to tell xmake to force it to be used as a gcc compiler. ::: --- --- url: /guide/extras/trybuild-3rd-sourcecode.md --- # Try building 3rd Sourcecode Xmake v2.3.1 and above directly interface with other third-party build systems. Even if other projects do not use xmake.lua for maintenance, Xmake can directly call other build tools to complete the compilation. So why use Xmake to call it if the user can directly use a third-party build tool to compile? The main benefits are: 1. Completely consistent behavior, simplifying the compilation process. No matter which other build system is used, you only need to execute the Xmake command to compile. Users no longer need to study the different compilation processes of other tools. 2. Docking the configuration environment of `xmake config`, reusing the platform detection and SDK environment detection of xmake, and simplifying the platform configuration. 3. Docking cross-compilation environment, even for projects maintained with Autotools, you can quickly cross-compile through Xmake. ## Supported build systems * Autotools (support for cross-compiling with Xmake) * XCodeBuild * CMake (support for cross-compiling with Xmake) * Make * MSBuild * Scons * Meson * Bazel * NdkBuild * Ninja ## Automatically detect build system and compile For example, for a project maintained using cmake, executing Xmake directly in the project root directory will automatically trigger a detection mechanism, detect CMakeLists.txt, and then prompt the user if cmake is needed to continue compiling. ```sh $ xmake note: CMakeLists.txt found, try building it (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) -- Symbol prefix: -- Configuring done -- Generating done -- Build files have been written to:/Users/ruki/Downloads/libpng-1.6.35/build [ 7%] Built target png-fix-itxt [ 21%] Built target genfiles [ 81%] Built target png [ 83%] Built target png_static ... output to/Users/ruki/Downloads/libpng-1.6.35/build/artifacts build ok! ``` ## Seamlessly, using the Xmake command Currently supports common commands such as `xmake clean`, `xmake --rebuild` and `xmake config` to seamlessly interface with third-party systems. We can directly clean the compiled output files of the cmake-maintained project ```sh $ xmake clean $ xmake clean --all ``` If you add `--all` to perform the cleanup, all files generated by autotools/cmake will be cleared, not only the object files. The default `xmake` is docked with incremental build behavior, but we can also force a quick rebuild: ```sh $ xmake --rebuild ``` ## Manually switch the specified build system If there are multiple build systems under maintenance in a project, such as the libpng project, which comes with Autotools/CMake/Makefile and other build system maintenance, Xmake defaults to using Autotools. If you want to force switch to other build systems, you can execute: ```sh $ xmake f --trybuild=[autotools|cmake|make|msbuild|..] $ xmake ``` In addition, the `--trybuild=` parameter is configured to manually specify the default build system, and the subsequent build process will not prompt the user for selection. ## Cross compile fast! As we all know, although many projects maintained by Autotools support cross-compilation, the configuration process of cross-compilation is very complicated. There are still many differences in different toolchain processing methods, and many pitfalls will be encountered in the process. Even if you run through a toolchain's cross-compilation, if you switch to another toolchain environment, it may take a long time, but if you use Xmake, you usually only need two simple commands: ::: tip NOTE At present, CMake/Autotools supports cross-compilation with Xmake. ::: ### Cross compile android platform ```sh $ xmake f -p android --trybuild=autotools [--ndk=xxx] $ xmake ``` ::: tip NOTE Among them, the --ndk parameter configuration is optional. If the user sets the ANDROID\_NDK\_HOME environment variable, or if the ndk is placed in ~/Library/Android/sdk/ndk-bundle, Xmake can automatically detect it. ::: Isn't it simple? If you think this is not much, then you can directly operate `./configure` to configure cross-compilation. You can see this document for comparison: [Using NDK with other compilation systems](https://developer.android.com/ndk/guides/other_build_systems#autoconf). To put it bluntly, you probably have to do this, you may not be able to do it once: ```sh $ export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG $ export AR=$TOOLCHAIN/bin/aarch64-linux-android-ar $ export AS=$TOOLCHAIN/bin/aarch64-linux-android-as $ export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang $ export CXX=$TOOLCHAIN/bin/aarch64-linux-android21-clang++ $ export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld $ export RANLIB=$TOOLCHAIN/bin/aarch64-linux-android-ranlib $ export STRIP=$TOOLCHAIN/bin/aarch64-linux-android-strip $ ./configure --host aarch64-linux-android $ make ``` If it is CMake, cross-compilation is not an easy task. For the Android platform, this configuration is required. ```sh $ cmake \ -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ -DANDROID_ABI=$ABI \ -DANDROID_NATIVE_API_LEVEL=$MINSDKVERSION \ $OTHER_ARGS ``` For the iOS platform, I did not find a short-answer configuration method, but found a third-party iOS toolchain configuration, which is very complicated: https://github.com/leetal/ios-cmake/blob/master/ios.toolchain.cmake For MinGW, it is another way. I have been tossing about the environment for a long time, which is very tossing. After using XMake, whether it is CMake or Autotools, cross-compilation is very simple, and the configuration method is exactly the same, streamlined and consistent. ### Cross compile iPhoneOS platform ```sh $ xmake f -p iphoneos --trybuild=[cmake|autotools] $ xmake ``` ### Cross-compile MinGW platform ```sh $ xmake f -p mingw --trybuild=[cmake|autotools] [--mingw=xxx] $ xmake ``` ### Using other cross-compilation toolchains ```sh $ xmake f -p cross --trybuild=[cmake|autotools] --sdk=/xxxx $ xmake ``` For more cross-compilation configuration details, please refer to the document: [Cross Compilation](/guide/basic-commands/cross-compilation), except for an additional `--trybuild=` parameter, all other cross-compilation configuration parameters are completely universal. ## Passing user configuration parameters We can use `--tryconfigs=` to pass additional configuration parameters of the user to the corresponding third-party build system. For example: autotools will be passed to `./configure`, cmake will be passed to the `cmake` command. ```sh $ xmake f --trybuild=autotools --tryconfigs="-enable-shared=no" $ xmake ``` For example, the above command passes `--enable-shared=no` to `./configure` to disable dynamic library compilation. In addition, for `--cflags`, `--includedirs` and `--ldflags`, you don't need to pass `--tryconfigs`, you can pass the built-in parameters like `xmake config --cflags=` to pass through. ## Examples of compiling other build system ### General Compilation In most cases, the compilation method after each docking system is consistent, except for the `--trybuild=` configuration parameter. ```sh $ xmake f --trybuild=[autotools|cmake|meson|ninja|bazel|make|msbuild|xcodebuild] $ xmake ``` ::: tip NOTE We also need to make sure that the build tool specified by --trybuild is installed and working properly. ::: ### Building Android JNI programs If `jni/Android.mk` exists in the current project, then Xmake can directly call NdkBuild to build the jni library. ```sh $ xmake f -p android --trybuild=ndkbuild [--ndk =] $ xmake ``` We also provided [xmake-gradle](https://github.com/xmake-io/xmake-gradle) to build the jni library in gradle, you can see [Use xmake to build JNI in Gradle](/guide/extensions/ide-integration-plugins#gradle-plugin-jni) --- --- url: /api/scripts/builtin-modules/try-catch-finally.md --- # 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. . --- --- url: /zh/api/scripts/builtin-modules/try-catch-finally.md --- # 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默认的处理程序接管,直接中断就行了。。 --- --- url: /guide/extras/unity-build.md --- # Unity Build Acceleration C++ compilation speed is usually very slow, because each file most likely includes headers, which include more headers, which include more headers, etc... In the end, a single C++ source file can exceed megabytes, all of which must be parsed for each file. That's a lot of duplicate work! With unity builds, we accelerate the compilation of the project by combining multiple cpp files into one. The main benefit is to reduce the repetitive work of parsing and compiling the contents of the header files contained in multiple source files. The contents of the header files usually account for most of the code in the source file after preprocessing. Unity build also reduces the overhead caused by having a large number of small source files by reducing the number of object files created and processed by the compilation chain, and allows inter-procedural analysis and optimization across files that form a unified build task (similar to optimization during effect linking). It can greatly improve the compilation speed of C/C++ code, usually by 30%. However, depending on the complexity of the project, the benefits it brings depend on the situation of the project. Xmake has also supported this build mode in v2.5.9. For related issues, see [#1019](https://github.com/xmake-io/xmake/issues/1019). ## How to enable it? We provide two built-in rules to handle Unity Build for C and C++ code respectively. ```lua -- Enable unity building for C add_rules("c.unity_build") -- Enable unity building for C++ add_rules("c++.unity_build") ``` ## Batch mode By default, as long as the above rules are set, Unity Build in Batch mode will be enabled, that is, xmake will automatically organize and merge according to the project code files. ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") ``` We can additionally specify the size of each merged batch by setting the `{batchsize = 2}` parameter to the rule, which means that every two C++ files are automatically merged and compiled. The compilation effect is roughly as follows: ```sh $ xmake -r [11%]: cache compiling.release build/.gens/test/unity_build/unity_642A245F.cpp [11%]: cache compiling.release build/.gens/test/unity_build/unity_bar.cpp [11%]: cache compiling.release build/.gens/test/unity_build/unity_73161A20.cpp [11%]: cache compiling.release build/.gens/test/unity_build/unity_F905F036.cpp [11%]: cache compiling.release build/.gens/test/unity_build/unity_foo.cpp [11%]: cache compiling.release build/.gens/test/unity_build/main.c [77%]: linking.release test [100%]: build ok ``` Since we only enabled the Unity Build of C++, the C code is still compiled one by one normally. In addition, in the Unity Build mode, we can still speed up the parallel compilation as much as possible without conflicting with each other. If the `batchsize` parameter is not set, all files will be merged into one file for compilation by default. ## Group Mode If the automatic merging effect of the above Batch mode is not satisfactory, we can also use custom grouping to manually configure which files are merged together to participate in the compilation, which makes users more flexible and gives more control. ```lua target("test") set_kind("binary") add_rules("c++.unity_build", {batchsize = 0}) - disable batch mode add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` We use `{unity_group = "foo"}` to specify the name of each group and which files are included. The files in each group will be merged into one code file separately. In addition, `batchsize = 0` also forcibly disables the Batch mode, that is, if there are no unity\_group grouped code files, we will still compile them separately, and will not automatically turn on automatic merging. ## Batch and Group mixed mode As long as we change the above `batchsize = 0` to a value other than 0, we can let the remaining code files continue to use the Batch mode in the grouping mode to automatically merge and compile. ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` ## Ignore the specified file If it is in Batch mode, because it is an automatic merge operation, all files will be merged by default, but if some code files do not want to participate in the merge, then we can also ignore them through `{unity_ignored = true}`. ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/test/*.c", {unity_ignored = true}) - ignore these files ``` ## Unique ID Although the benefits of Unity Build are good, we still encounter some unexpected situations. For example, in our two code files, under the global namespace, there are global variables and functions with the same name. Then, merge compilation will bring about compilation conflicts, and the compiler usually reports global variable redefinition errors. In order to solve this problem, we need to make some modifications to the user code, and then cooperate with the build tool to solve it. For example, our `foo.cpp` and `bar.cpp` both have a global variable i. ```cpp /* foo.cpp */ namespace { int i = 42; }; int foo(void) { return i; } ``` ```cpp /* bar.cpp */ namespace { int i = 42; }; int bar(void) { return i; } ``` Then, our merge compilation will conflict, and we can introduce a Unique ID to isolate the global anonymous space. ```cpp /* foo.cpp */ namespace MY_UNITY_ID { int i = 42; }; int foo(void) { return MY_UNITY_ID::i; } ``` bar.cpp ```cpp namespace MY_UNITY_ID { int i = 42; }; int bar(void) { return MY_UNITY_ID::i; } ``` Next, we also need to ensure that after the code is merged, the definitions of `MY_UNITY_ID` in foo and bar are completely different, and a unique ID value can be calculated according to the file name, which does not conflict with each other. This is to achieve the following merge effect: ```c #define MY_UNITY_ID #include "foo.c" #undef MY_UNITY_ID #define MY_UNITY_ID #include "bar.c" #undef MY_UNITY_ID ``` This may seem troublesome, but the user does not need to care about these; Xmake will automatically process them when merging! The user only needs to specify the name of the Unique ID, for example, the following: ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2, uniqueid = "MY_UNITY_ID"}) add_files("src/*.c", "src/*.cpp") ``` Dealing with global variables, as well as global macro definitions with the same name, functions, etc., can be used in this way to avoid conflicts. --- --- url: /zh/guide/extras/unity-build.md --- # Unity 编译加速 {#unity-build-acceleration} 我们知道,C++ 代码编译速度通常很慢,因为每个代码文件都需要解析引入的头文件。 而通过 Unity Build,我们通过将多个 cpp 文件组合成一个来加速项目的编译,其主要好处是减少了解析和编译包含在多个源文件中的头文件内容的重复工作,头文件的内容通常占预处理后源文件中的大部分代码。 Unity 构建还通过减少编译链创建和处理的目标文件的数量来减轻由于拥有大量小源文件而导致的开销,并允许跨形成统一构建任务的文件进行过程间分析和优化(类似于效果链接时优化)。 它可以极大提升 C/C++ 代码的编译速度,通常会有 30% 的速度提升,不过根据项目的复杂程度不同,其带来的效益还是要根据自身项目情况而定。 xmake 在 v2.5.9 版本中,也已经支持了这种构建模式。相关 issues 见 [#1019](https://github.com/xmake-io/xmake/issues/1019)。 ## 如何启用?{#how-to-enable-it} 我们提供了两个内置规则,分别处理对 C 和 C++ 代码的 Unity Build。 ```lua add_rules("c.unity_build") add_rules("c++.unity_build") ``` ## Batch 模式 {#batch-mode} 默认情况下,只要设置上述规则,就会启用 Batch 模式的 Unity Build,也就是 xmake 自动根据项目代码文件,自动组织合并。 ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") ``` 我们可以额外通过设置 `{batchsize = 2}` 参数到规则,来指定每个合并 Batch 的大小数量,这里也就是每两个 C++ 文件自动合并编译。 编译效果大概如下: ```sh $ xmake -r [ 11%]: cache compiling.release build/.gens/test/unity_build/unity_642A245F.cpp [ 11%]: cache compiling.release build/.gens/test/unity_build/unity_bar.cpp [ 11%]: cache compiling.release build/.gens/test/unity_build/unity_73161A20.cpp [ 11%]: cache compiling.release build/.gens/test/unity_build/unity_F905F036.cpp [ 11%]: cache compiling.release build/.gens/test/unity_build/unity_foo.cpp [ 11%]: cache compiling.release build/.gens/test/unity_build/main.c [ 77%]: linking.release test [100%]: build ok ``` 由于我们仅仅启用了 C++ 的 Unity Build,所以 C 代码还是正常挨个编译。另外在 Unity Build 模式下,我们还是可以做到尽可能的并行编译加速,互不冲突。 如果没有设置 `batchsize` 参数,那么默认会把所有文件合并到一个文件中进行编译。 ## Group 模式 {#group-mode} 如果上面的 Batch 模式自动合并效果不理想,我们也可以使用自定义分组,来手动配置哪些文件合并到一起参与编译,这使得用户更加地灵活可控。 ```lua target("test") set_kind("binary") add_rules("c++.unity_build", {batchsize = 0}) -- disable batch mode add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` 我们使用 `{unity_group = "foo"}` 来指定每个分组的名字,以及包含了哪些文件,每个分组的文件都会单独被合并到一个代码文件中去。 另外,`batchsize = 0` 也强行禁用了 Batch 模式,也就是说,没有设置 unity\_group 分组的代码文件,我们还是会单独编译它们,也不会自动开启自动合并。 ## Batch 和 Group 混合模式 {#batch-and-group-mode} 我们只要把上面的 `batchsize = 0` 改成非 0 值,就可以让分组模式下,剩余的代码文件继续开启 Batch 模式自动合并编译。 ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` ## 忽略指定文件 {#ignore-files} 如果是 Batch 模式下,由于是自动合并操作,所以默认会对所有文件执行合并,但如果有些代码文件我们不想让它参与合并,那么我们也可以通过 `{unity_ignored = true}` 去忽略它们。 ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/test/*.c", {unity_ignored = true}) -- ignore these files ``` ## Unique ID 尽管 Unity Build 带啦的收益不错,但是我们还是会遇到一些意外的情况,比如我们的两个代码文件里面,全局命名空间下,都存在相同名字的全局变量和函数。 那么,合并编译就会带来编译冲突问题,编译器通常会报全局变量重定义错误。 为了解决这个问题,我们需要用户代码上做一些修改,然后配合构建工具来解决。 比如,我们的 foo.cpp 和 bar.cpp 都有全局变量 i。 foo.cpp ```c namespace { int i = 42; } int foo() { return i; } ``` bar.cpp ```c namespace { int i = 42; } int bar() { return i; } ``` 那么,我们合并编译就会冲突,我们可以引入一个 Unique ID 来隔离全局的匿名空间。 foo.cpp ```c namespace MY_UNITY_ID { int i = 42; } int foo() { return MY_UNITY_ID::i; } ``` bar.cpp ```c namespace MY_UNITY_ID { int i = 42; } int bar() { return MY_UNITY_ID::i; } ``` 接下来,我们还需要保证代码合并后, `MY_UNITY_ID` 在 foo 和 bar 中的定义完全不同,可以按文件名算一个唯一 ID 值出来,互不冲突,也就是实现下面的合并效果: ```c #define MY_UNITY_ID #include "foo.c" #undef MY_UNITY_ID #define MY_UNITY_ID #include "bar.c" #undef MY_UNITY_ID ``` 这看上去似乎很麻烦,但是用户不需要关心这些,xmake 会在合并时候自动处理它们,用户只需要指定这个 Unique ID 的名字就行了,例如下面这样: ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2, uniqueid = "MY_UNITY_ID"}) add_files("src/*.c", "src/*.cpp") ``` 处理全局变量,还有全局的重名宏定义,函数什么的,都可以采用这种方式来避免冲突。 --- --- url: /about/contact.md --- * Email: * Homepage: [xmake.io](https://xmake.io) * Community * [Chat on Reddit](https://www.reddit.com/r/xmake/) * [Chat on Telegram](https://t.me/tbooxorg) * [Chat on Discord](https://discord.gg/xmake) * Chat on QQ Group: 343118190, 662147501 * Source Code: [GitHub](https://github.com/xmake-io/xmake), [Gitee](https://gitee.com/tboox/xmake) * WeChat Public: tboox-os --- --- url: /about/sponsor.md --- The Xmake projects are personal open-source projects, and their development needs your help. If you would like to support the development of Xmake, you are encouraged to donate! Feel free to contact us for more details about sponsorship. * [Contact Us](/about/contact) ## Payment Methods ### PayPal [![PayPal Me](/assets/img/paypal.png)](https://paypal.me/tboox/5) ### GitHub Sponsor [![GitHub Sponsor](/assets/img/github_sponsor.png)](https://github.com/sponsors/waruqi) ### OpenCollective \[[Become a Sponsor](https://opencollective.com/xmake#sponsor)] ### Alipay ### WeChat ## Sponsor Tiers ### $5 a Month ☕️ - Buy me a coffee every month. * You will receive a sponsor badge on your profile! ### $10 a Month 🙏 - Thank you so much! For sponsoring me, you'll receive: * All previous tier rewards * Membership in the Discord channel is marked as sponsor * Your name added to the backers section on my personal website ### $20 a Month 🍨 - Thank you so much! For sponsoring me, you'll receive: * All previous tier rewards * Handling issues with higher priority ### $50 a Month 🐹 - Thank you so much! For sponsoring me, you'll receive: * All previous tier rewards * Review your `xmake.lua` and provide suggestions for improvement ### $200 a Month 🐴 - Thank you so much! That's a lot of money. For sponsoring me, you'll receive: * All previous tier rewards * Add a logo and link to the README.md of my major open-source projects * One-to-one technical consulting service ### $500 a Month 🐬 - Should you really be spending this much? For sponsoring me, you'll receive: * All previous tier rewards * Add a large logo and link to the websites of my major open-source projects ### $1000 a Month ❤️ - How do you have this much money to spend? For sponsoring me at this incredible tier, you'll receive: * All previous tier rewards * Please contact me to discuss what you want for this tier! ## Supporters | 日期 | 支持者 | 渠道 | 金额 | 评价 | |-- | -- | -- | -- | -- | | 2024.04.08 | Maxwell | 微信 | ¥1024 | | | 2024.03.15 | h\*] | 微信 | ¥20 | | | 2024.03.17 | TomCat | 支付宝 | ¥20 | 感谢作者啊,请你喝咖啡 | | 2024.03.13 | H\*s | 微信 | ¥5 | | | 2024.03.08 | \*秀 | 微信 | ¥20 | | | 2024.03.02 | *丘 | 微信 | ¥100 | | | 2024.02.29 | archer正 | 支付宝 | ¥5 | 感谢群主回答我问题,解了急事 | | 2024.02.26 | s*e | 微信 | ¥50 | | | 2024.02.17 | *鸟 | 微信 | ¥3 | | | 2024.02.08 | T*7 | 微信 | ¥10 | | | 2024.01.31 | 振豪 | 支付宝 | ¥50 | 苦 cmake 久已,支持国产 xmake | | 2024.01.11 | \*生 | 微信 | ¥100 | | | 2023.12.29 | *业 | 微信 | ¥10 | | | 2023.12.23 | D*t | 微信 | ¥150 | | | 2023.12.12 | *非 | 微信 | ¥50 | | | 2023.11.23 | jg | 支付宝 | ¥10 | | | 2023.11.22 | doukeran | 支付宝 | ¥10 | 加油!xmake好用,感谢! | | 2023.11.17 | jadedrip | 支付宝 | ¥5 | | | 2023.11.14 | \* | 微信 | ¥50 | | | 2023.11.13 | 玮 | 支付宝 | ¥10 | 支持国产软件! | | 2023.11.08 | 灵均 | 支付宝 | ¥100 | 祝阖家安康,xmake越来越好用! | | 2023.11.05 | 走钢丝 | 支付宝 | ¥168 | 用了下,感觉不错! | | 2023.10.27 | *利 | 微信 | ¥100 | | | 2023.10.18 | F2 | 微信 | ¥20 | | | 2023.10.05 | C*g | 微信 | ¥5 | | | 2023.09.28 | \* | 微信 | ¥50 | | | 2023.09.22 | \* | 微信 | ¥50 | | | 2023.09.20 | V*e | 微信 | ¥20 | | | 2023.09.17 | \*阴 | 微信 | ¥20 | | | 2023.09.07 | \*物 | 微信 | ¥20 | | | 2023.08.23 | 黑白 | 支付宝 | ¥20 | 开源不易,请大佬喝杯奶茶 | | 2023.08.17 | ivanllen | 微信 | ¥200 | 感谢 ruki,给了很多帮助 | | 2023.08.11 | tjan | 支付宝 | ¥58 | | | 2023.08.06 | \*好 | 微信 | ¥20 | 非常感谢作者维护这样一个好用的构建工具 | | 2023.08.06 | *子 | 微信 | ¥20 | 感谢开发和维护 xmake | | 2023.07.30 | *东 | 微信 | ¥100 | | | 2023.07.26 | Deep鑫 | 支付宝 | ¥200 | 感谢答疑,希望 xmake 越来越好 | | 2023.07.22 | 候*7 | 微信 | ¥70 | | | 2023.07.22 | 志超 | 支付宝 | ¥100 | 支持一下 | | 2023.07.17 | *狼 | 微信 | ¥106 | | | 2023.07.07 | Neo | 微信 | ¥10 | 希望 xmake 越做越好 | | 2023.06.21 | 小鸡快跑 | 微信 | ¥10 | 是比 cmake 更好用的构建工具 | | 2023.06.20 | 阿诺 | 支付宝 | ¥200 | 希望 xmake 越来越好 | | 2023.06.17 | 葡* | 微信 | ¥29.9 | | | 2023.06.06 | 蓝颜 | 支付宝 | ¥50 | xmake 很好用,已经推荐给朋友们用 | | 2023.06.04 | i*t | 微信 | ¥18.88 | | | 2023.05.18 | 晚风 | 支付宝 | ¥20 | 支持! | | 2023.05.15 | *侠 | 微信 | ¥19.57 | | | 2023.04.24 | 好吧,注册一下 | 支付宝 | ¥100 | xmake 天下第一 | | 2023.04.21 | 悬阳 | 支付宝 | ¥50 | | | 2023.04.17 | 文强 | 微信 | ¥19.88 | | | 2023.04.13 | *焜 | 微信 | ¥50 | | | 2023.03.19 | *鸟 | 微信 | ¥3 | | | 2023.03.18 | *戴 | 微信 | ¥100 | 感谢 xmake | | 2023.03.10 | 翅膀 | 微信 | ¥200 | 支持一下 xmake | | 2023.02.05 | l*2 | 微信 | ¥300 | | | 2023.02.02 | \* | 微信 | ¥5 | 国产加油 | | 2023.02.01 | *X | 微信 | ¥256 | | | 2023.01.31 | *乐 | 微信 | ¥66 | cmake 反人类设计,这下清爽了,顶! | | 2023.01.18 | r*t | 微信 | ¥100 | | | 2023.01.17 | Maxwell | 微信 | ¥1024 | | | 2023.01.16 | 繁星明 | 微信 | ¥50 | 好用 | | 2023.01.11 | 知而无知 | 微信 | ¥50 | xmake加油! | | 2023.01.11 | 熊猫儿沈浪 | 微信 | ¥100 | | | 2022.12.22 | 我出趟远门 | 支付宝 | ¥100 | | | 2022.12.14 | 1*1 | 微信 | ¥77 | | | 2022.12.02 | l*u | 微信 | ¥16.8 | 加油 | | 2022.11.30 | 高洁 | 支付宝 | ¥50 | 极大减轻了构建c++项目的成本 | | 2022.11.19 | *埃 | 微信 | ¥50 | | | 2022.11.11 | 王王王 | 支付宝 | ¥100 | | | 2022.10.26 | D*w | 微信 | ¥30 | 支持好用,希望越做越好 | | 2022.10.11 | N*e | 微信 | ¥50 | 感谢大佬的工作 | | 2022.09.24 | *谔 | 微信 | ¥100 | 加油 | | 2022.08.31 | 九州 | 支付宝 | ¥100 | 用了一年,感谢作者的辛苦 | | 2022.07.24 | 未来十年 | 支付宝 | ¥50 | xmake 最终会统一 C++ 包管理 | | 2022.07.24 | *大 | 微信 | ¥50 | 赞助 xmake 项目 | | 2022.07.17 | 王逸涵 | 支付宝 | ¥100 | 支持!!! | | 2022.07.17 | Quincy | 微信 | ¥200 | 开源不易,xmake 非常好用 | | 2022.07.09 | *洋 | 微信 | ¥66 | | | 2022.07.07 | 卷云舒 | 支付宝 | ¥100 | 支持国产支持开源!加油兄弟!我是知乎上的 | | 2022.07.07 | 魏文* | 支付宝 | ¥100 | 体验比cmake丝滑多了 | | 2022.06.30 | 小明 | 支付宝 | ¥30 | 加油 | | 2022.06.28 | Bain | 支付宝 | ¥10 | | | 2022.06.22 | 魏文* | 支付宝 | ¥100 | 支持国产,C++开源界因xmake更美好~ | | 2022.06.14 | K*u | 微信 | ¥10 | | | 2022.06.08 | \* | 微信 | ¥81.54 | | | 2022.05.11 | *庭 | 微信 | ¥50 | | | 2022.04.28 | m*l | 微信 | ¥99 | | | 2022.04.21 | 寻找地平线 | 微信 | ¥128 | | | 2022.04.09 | 子慧 | 支付宝 | ¥50 | 非常好用,简洁清晰 | | 2022.04.07 | \* | 微信 | ¥100 | | | 2022.04.05 | \* | 微信 | ¥5 | | | 2022.03.30 | \* | 微信 | ¥5 | | | 2022.03.05 | \* | 微信 | ¥15 | | | 2022.02.04 | *雯 | 支付宝 | ¥10 | 加油 | | 2022.01.25 | 龙伟 | 支付宝 | ¥10 | 生成的目录结构确实比 cmake 强 | | 2021.12.24 | xiaohui | 微信 | ¥50 | | | 2021.12.14 | SirLynix | Github | $10 | | | 2021.12.14 | sytru | 支付宝 | ¥50 | | | 2021.12.14 | 翅膀 | 微信 | ¥100 | | | 2021.11.15 | 朱* | 支付宝 | ¥50 | xmake非常好用,继续加油! | | 2021.10.04 | S*o | 微信 | ¥100 | 加油 | | 2021.09.23 | fhhddgkhg | 支付宝 | ¥20 | | | 2021.09.06 | Stefan Boberg | Paypal | € 25 | | | 2021.09.01 | 姚冬 | 微信 | ¥1024 | 祝 xmake 越做越好 | | 2021.08.22 | Alvin-co | 微信 | ¥100 | | | 2021.08.17 | 九州 | 支付宝 | ¥100 | | | 2021.07.29 | chaney | 支付宝 | ¥1000 | xmake 很好用 | | 2021.06.30 | \*剑 | 微信 | ¥100 | 大佬牛逼,给你点个赞 | | 2021.06.08 | Daniel Roussel | Paypal | 5 EUR | | | 2021.05.31 | 国栋 | 支付宝 | ¥100 | 加油 ^ O ^ ~ 做的好! | | 2021.05.26 | jerry | 支付宝 | ¥100 | 感谢对开源世界的帮助! | | 2021.05.02 | 乐 | 支付宝 | ¥20 | | | 2021.04.26 | 灿辉 | 支付宝 | ¥20 | | | 2021.04.04 | \*烨 | 微信 | ¥50 | | | 2021.03.31 | *晨 | 微信 | ¥10 | | | 2021.03.18 | *博 | 微信 | ¥50 | | | 2021.03.17 | *博 | 微信 | ¥100 | | | 2021.03.14 | Gavin Ray | OpenCollective | $10 | | | 2021.02.07 | 抚* | 支付宝 | ¥66 | xmake牛批! | | 2021.02.05 | *\_ | 微信 | ¥5 | | | 2020.12.05 | 知而无知 | 微信 | ¥50 | | | 2020.12.05 | fghuh | 支付宝 | ¥10 | | | 2020.12.05 | @ | 微信 | ¥5 | | | 2020.11.27 | 少东 | 支付宝 | ¥2.33 | | | 2020.11.25 | 小弧光 | 微信 | ¥10 | | | 2020.11.20 | Russell Haley | Paypal | $25CAD | | | 2020.11.11 | Russell Haley | Paypal | $50CAD | | | 2020.10.23 | Cartesian Technology | Paypal | 5 EUR | | | 2020.10.19 | Cartesian Technology | Paypal | 5 EUR | | | 2020.09.22 | Russell Haley | Paypal | $25CAD | | | 2020.09.13 | Cartesian Technology | Paypal | 5 EUR | | | 2020.09.11 | Cartesian Technology | Paypal | 5 EUR | | | 2020.09.11 | 复刻回忆 | 支付宝 | ¥25 | 加油! | | 2020.09.05 | M*x | 微信 | ¥100 | | | 2020.07.21 | 简单 | 微信 | ¥100 | | | 2020.06.24 | *三 | 微信 | ¥10 | xmake很好用 | | 2020.06.13 | Ronald | 支付宝 | ¥10 | 方便、专业、谢谢 | | 2020.06.13 | c*o | 微信 | ¥100 | | | 2020.06.09 | w*; | 微信 | ¥50 | 我用过最舒心的工具 | | 2020.06.09 | 凌风 | 支付宝 | ¥0.01 | | | 2020.05.25 | 魔之左手 | 微信 | ¥100 | | | 2020.05.20 | Russell Haley | Paypal | $10CAD | | | 2020.05.15 | Russell Haley | Paypal | $10CAD | | | 2020.04.10 | C*g | 微信 | ¥20 | 拒绝白嫖,支持一下 | | 2020.04.07 | *子 | 微信 | ¥10 | 感谢你创造了xmake | | 2019.10.31 | 刘* | 支付宝 | ¥100 | xmake非常好用,期待持续更新,加油! | | 2019.10.05 | 1m188 | 支付宝 | ¥50 | 相比cmake写起来要简单清晰一些 | | 2019.09.15 | ryan | 微信 | ¥100 | | | 2019.06.19 | 匿名 | 微信 | ¥10 | 比cmake好用 | | 2018.11.16 | 孙果 | 微信 | ¥10 | xmake很好用,加油! | | 2018.10.17 | 红红 | 微信 | ¥100 | | | 2018.10.15 | xtvjxk | 支付宝 | ¥10 | 希望越来越好用 | | 2018.08.07 | fasiondog | gitee | ¥50 | 感谢您的开源项目! | | 2018.06.27 | Kellygod | 支付宝 | ¥30 | 拥抱xmake | | 2018.05.17 | 匿名 | 微信 | ¥10 | 加油xmake | | 2018.04.29 | 清春 | 支付宝 | ¥180 | | | 2018.02.18 | 氧烷 | 支付宝 | ¥16.66 | xmake,赞👍 | | 2017.11.19 | sytru | 支付宝 | ¥10 | 感谢作者,让写c/c++有了写脚本的感觉。非常愉悦的使用体验,希望不要消失。 | | 2017.11.16 | 琪峻 | 支付宝 | ¥15 | 感谢好用的xmake | | 2017.10.26 | Welthy | 支付宝 | ¥5 | xmake很好用啊 | | 2016.11.10 | [lc-soft](https://github.com/lc-soft) | oschina | ¥10 | | --- --- url: /about/who_is_using_xmake.md --- If you are using Xmake, please click to edit [this page](https://github.com/xmake-io/xmake-docs/edit/master/docs/about/who_is_using_xmake.md) to submit your information to the following list, so more users can know how many people are using Xmake. This also allows users to use Xmake more confidently, and gives us more motivation to maintain it continuously, so that the Xmake project and community will grow stronger. ::: tip NOTE If it is inconvenient for you to add your project address, you can briefly describe the usage scenarios of your project. ::: ## User List | User (Company Name or Personal Contact) | Project (Project Introduction or Address) | Comment (optional) | | :--- | :--- | :--- | | \[Aleksandr Alibekov] | [TurboINI](https://github.com/HotWizard/TurboINI) | Xmake is so simple and nice to use. | | [Isaac A.](https://gitlab.com/tenten8401) | [VueFusion](https://gitlab.com/tenten8401/vuefusion) VR Full Body Tracker | Xmake is awesome | | [ruki](https://github.com/waruqi) | [tbox](https://github.com/tboox/tbox) & [ltui](https://github.com/tboox/ltui) | ... | | [zsx](https://github.com/acl-dev/acl) | [acl](https://github.com/acl-dev/acl) | Good | | [深圳市云语科技](https://www.raysync.cn/) | [镭速-大文件传输系统](https://www.raysync.cn/) | 非常有特点的构建系统 | | [深圳金鼎威视](http://www.gzgve.com/) | 暂时都是内部项目 | 嵌入式开发中目前用过的最优秀的c程序构建工具 | | [WillyJL](https://github.com/Willy-JL) | [CP77 Discord RPC](https://github.com/Willy-JL/cp77-discord-rpc) ([Homepage](https://www.nexusmods.com/cyberpunk2077/mods/986)) | Perfect and easy way to get into C++ without prior experience | | [SirLynix](https://github.com/SirLynix) | [BurgWar](https://github.com/DigitalPulseSoftware/BurgWar) (Video game) & [obs-kinect](https://github.com/SirLynix/obs-kinect) (plugin for OBS) | After programming for more than 12 years in C++, I can say xmake is a life changer. It's to C++ what Cargo is to Rust, a truly helpful tool (especially with external dependencies) you won't want to give up. | [yamashi](https://github.com/yamashi) | [Cyber Engine Tweaks](https://github.com/yamashi/CyberEngineTweaks) (Mod for Cyberpunk 2077) | Dll injection to add Lua scripting, imgui user interface, console cheats and other hacks to Cyberpunk 2077. | [Tilted Phoques](https://github.com/tiltedphoques) | [Skyrim/Fallout Together](https://github.com/tiltedphoques/TiltedOnline/) (Mod for Skyrim & Fallout 4) | Dll injection to add multiplayer to CreationKit games such as Skyrim and Fallout 4. | [Qudix](https://github.com/Qudix) | [template-commonlibsse-ng](https://github.com/Qudix/template-commonlibsse-ng) | SKSE64 template using CommonLibSSE-NG | | [武汉凌久微电子有限公司](http://www.ljmicro.cn) | 内部嵌入式图形驱动项目 | 支持翼辉、天脉、锐华等多种国产嵌入式实时操作系统的GP100/200系列国产GPU OpenGL/ES驱动 | | [yecate](https://github.com/yecate) | 个人项目 | 跨平台构建的利器,xmake在我日常开发工作中提供了很大的便利 | | [fasiondog](https://github.com/fasiondog) | [Hikyuu Quant Framework](https://github.com/fasiondog/hikyuu) 基于C++/Python的开源量化交易研究框架 | xmake 的简洁高效是C/C++跨平台工程的解放 | | [KnightJun](https://github.com/knightjun) | [BingW](https://github.com/knightjun/BingW)(bing wallpaper) | xmake is an excellent, simple, easy-to-use and powerful build tool. | [Phate6660](https://github.com/Phate6660) | [cppfetch](https://github.com/Phate6660/cppfetch) (info fetch tool) | I tried this out after seeing [this](https://github.com/xmake-io/xmake/wiki/C-and-Cplusplus-build-system,-I-use-xmake), and after using it I loved it enough that I replaced meson with this! | walletiger | ERTC - an easy and efficient cross platform RTC project | 个人项目,简单易用跨平台 兼容 webrtc 标准的 SFU及前端通信组件 , xmake + tbox 提供了优秀的跨平台编译和底层库支持| | [Alvin](https://github.com/idealvin) | [co](https://github.com/idealvin/co) | A go-style coroutine library in C++11. Xmake is nice! | | [ekko](https://github.com/ekkone) | 电网融合终端linux应用,个人使用 | 相比Makefile和Cmake简洁易懂太多了,构建十分高效,并发编译速度比公司用的cmake快一个数量级,希望发展越来越好 | | [合宙通信](https://www.openluat.com/) | https://gitee.com/openLuat/luatos-soc-air101 | xmake 是非常简洁,高效,方便的C/C++构建工具,节省很多时间 | | [Akselmo](https://codeberg.org/akselmo) | [Artificial Rage](https://codeberg.org/akselmo/artificial-rage), a simple FPS game and (probably) an FPS game engine in future | Switched from cmake to xmake. The ease of use is great, and being able to generate cmake files for tools that require it is a cherry on top. Highly recommend! | [JackLan](https://github.com/lanjackg2003) | 目前,尝试在个人嵌入式项目中使用 | 在几年前,使用make与makefile实现了与XMake类似的C/C++项目构建功能。现在回想起来,从零到有,整个流程也并非那么一帆风顺。最近想使用CMake重新实现这套构建,无意中发现了XMake。让我眼前一亮的是,他使用LUA语法(早些年从合宙的GSM模块玩起来),容易上手。我将之前的Make项目移植使用XMake工具,也就是几小时,这个过程也只是我从第一次参考官方说明做起。所以确实省时省力。希望XMake能够让更多的开发者知道与使用,成为更加主流的构建工具! | [XmacsLabs](https://github.com/XmacsLabs) | [mogan](https://github.com/XmacsLabs/mogan) | autotool和CMake开发环境不易配置,vcpkg等CMake包管理器存在不少兼容性问题,极度影响开发体验,导致墨者实验室失去了一大批潜在开发者。xmake为老旧软件的维护提供了紧凑易用的工具,有效简化了添加新类库的流程,减少了无谓的工作量。 | [Dozingfiretruck](https://github.com/Dozingfiretruck) | C语言实现的NES模拟器 GitHub地址: https://github.com/Dozingfiretruck Gitee地址: https://gitee.com/Dozingfiretruck/nes | xmake 可以使你极其方便的部署C/C++构建,节省大量时间从而将精力放在代码编写上而非项目构建上 | | [WSSDude](https://github.com/WSSDude) | [Hitman Audio Tool](https://github.com/WSSDude/HitmanAudioTool) | Very easy to use build system with what I believe is currently the best package management integrated within. Definitely plan to include it in my other future projects (both personal and public). Using it since 2020 and counting. | | Meidozuki | [VBAO](https://github.com/Meidozuki/VBAO) | 个人开发项目,用于构建MVVM开发框架的utility library,提供C++和python接口。xmake的语法相比cmake十分简洁,尤其是处理包依赖问题时:cmake的find\_package(尤其是Config Mode)依赖于第三方库作者的水平,xmake接入了Conan包管理,大大简化了这一过程;对于Makefile-only的项目,cmake需要额外了解IMPORTED library target的概念,十分别扭,xmake的写法就很舒服。虽然xmake的命令行已经足够简洁了,不过还是期待主流IDE对于xmake更好的支持 | | Luisa-Group | [Luisa-Compute](https://luisa-render.com/) | A general-purpose, high-performance, cross-platform computing framework for streaming-architectures. Thanks to xmake's cross-platform ability and lua DSL's flexible expressioness, the development efficiency and compilation experience has been greatly improved. | | [Sunrisepeak](https://github.com/Sunrisepeak) | [DStruct](https://github.com/Sunrisepeak/DStruct) (an easy-to-port/learn/use C++ data structure template library) | DStruct 是一个平台无关(易于移植)且结构简洁的数据结构模板库, 可使用xmake进行快速构建&测试 | | [chen-qingyu](https://github.com/chen-qingyu) | [PyInCpp](https://github.com/chen-qingyu/pyincpp) (A C++ type library that is as easy to use as Python built-in types.) | Thanks to XMake: Code & Format & Document & Build & Test & Pack all in one, elegant and efficient! | | [Sunrisepeak](https://github.com/Sunrisepeak) | [KHistory](https://github.com/Sunrisepeak/KHistory) (An elegant keyboard/gamepad key detection and visualization tool) | 🔥一个优雅&跨平台的 键盘/🎮手柄按键 检测及历史记录显示工具, 无需安装单可执行文件(约900kb大小)即点即用, 并提供了可视化插件扩展功能, 同时也由于xmake对lua脚本的支持&构建规则, 以简洁的方式在编译期实现了自定义插件的自动注册功能 | | [laugh12321](https://github.com/laugh12321) | [TensorRT-YOLO](https://github.com/laugh12321/TensorRT-YOLO) |🚀 TensorRT-YOLO: Support YOLOv5, YOLOv8, YOLOv9, PP-YOLOE using TensorRT acceleration with EfficientNMS! TensorRT-YOLO uses xmake for compilation, enabling developers to focus more on coding rather than on the build process.| | [dkaszews](https://github.com/dkaszews) | [riscv-xmake-samples](https://github.com/dkaszews/riscv-xmake-samples) | Sample code using RISC-V bare-metal assembly and xmake build system | | [JXMaster](https://github.com/JX-Master) | [LunaSDK](https://github.com/JX-Master/LunaSDK) | A C++ software development framework for real-time rendering applications. | --- --- url: /examples/bindings/nodejs-module.md --- Refer [example](https://github.com/tonyfettes/coc-rime/blob/master/xmake.lua). ```lua add_rules("mode.debug", "mode.release") add_requires("node-addon-api") target("rime") set_languages("cxx17") add_rules("nodejs.module") add_packages("node-addon-api") add_files("*.cc") ``` --- --- url: /examples/bindings/lua-module.md --- Refer [luarocks-build-xmake](https://github.com/xmake-io/luarocks-build-xmake) If your lua module contains C code, you can use [LuaNativeObjects](https://github.com/Neopallium/LuaNativeObjects) to generate C code from lua code. Refer [example](https://github.com/Freed-Wu/rime.nvim/blob/main/xmake.lua). ```lua add_rules("mode.debug", "mode.release") target("rime") add_rules("lua.module", "lua.native-objects") add_files("*.nobj.lua") add_cflags("-Wno-int-conversion") ``` --- --- url: /examples/bindings/swig.md --- Version 2.5.8 supports the construction of Swig modules. We provide `swig.c` and `swig.cpp` rules, which respectively support the generation of c/c++ module interface code, and cooperate with xmake's package management system to realize fully automated modules and dependent packages. Integration. Related issues: [#1622](https://github.com/xmake-io/xmake/issues/1622) ## Lua/C module ```lua add_rules("mode.release", "mode.debug") add_requires("lua") target("example") add_rules("swig.c", {moduletype = "lua"}) add_files("src/example.i", {swigflags = "-no-old-metatable-bindings"}) add_files("src/example.c") add_packages("lua") ``` ## Python/C module ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.c", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.c") add_packages("python") ``` ## Python/C++ module ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.cpp", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.cpp") add_packages("python") ``` ## Java/C module [Example project](https://github.com/xmake-io/xmake/blob/dev/tests/projects/swig/java_c) ```lua -- make sure you config to an enviroment with jni.h -- for example: xmake f -c -p android target("example") set_kind('shared') -- set moduletype to java add_rules("swig.c", {moduletype = "java"}) -- test jar build -- add_rules("swig.c", {moduletype = "java" , buildjar = true}) -- use swigflags to provider package name and output path of java files add_files("src/example.i", {swigflags = { "-package", "com.example", "-outdir", "build/java/com/example/" }}) add_files("src/example.c") add_includedirs("src") before_build(function() -- ensure output path exists before running swig os.mkdir("build/java/com/example/") end) ``` We can also configure `buildjar = true` to build jar file. ```lua add_rules("swig.c", {moduletype = "java", buildjar = true}) ``` --- --- url: /examples/cpp/asn1.md --- ASN.1 programs need to use [ASN.1 Compiler](https://github.com/vlm/asn1c) to generate relevant .c files to participate in project compilation. While Xmake provides built-in `add_rules("asn1c")` rules to process `.c` file generation, `add_requires("asn1c")` automatically pulls and integrates ASN.1 compiler tools. Here is a basic configuration example: ```lua add_rules("mode.debug", "mode.release") add_requires("asn1c") target("test") set_kind("binary") add_files("src/*.c") add_files("src/*.asn1") add_rules("asn1c") add_packages("asn1c") ``` For details, see [Example Project](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/asn1c). --- --- url: /examples/cpp/basic.md --- We briefly introduce some commonly used project examples. More and more complete examples projects can be viewed in [project examples](https://github.com/xmake-io/xmake/tree/master/tests/projects). We can also use the `xmake create` command to create various commonly used empty projects to quickly start. For the introduction of this command and the supported project templates, you can type the following command to view: ```sh xmake create --help ``` ## Executable Program ```lua target("test") set_kind("binary") add_files("src/*.cpp") ``` For a complete example, execute the following command to create: ```sh xmake create test ``` If we want to create c language program. We can add `-l c` argument. for example: ```sh xmake create -l c test ``` ## Static Library Program ```lua target("foo") set_kind("static") add_files("src/foo/*.cpp") target("test") set_kind("binary") add_files("src/*.cpp") add_deps("foo") ``` We use `add_deps` to link a static library to test target. For a complete example, execute the following command to create: ```sh xmake create -t static test ``` If we want to create c language program. We can add `-l c` argument. for example: ```sh xmake create -l c static test ``` ## Shared Library Program ```lua target("foo") set_kind("shared") add_files("src/foo/*.cpp") target("test") set_kind("binary") add_files("src/*.cpp") add_deps("foo") ``` We use `add_deps` to link a shared library to test target. For a complete example, execute the following command to create: ```sh xmake create -t shared test ``` If we want to create c language program. We can add `-l c` argument. for example: ```sh xmake create -l c shared test ``` --- --- url: /examples/cpp/cosmocc.md --- ```lua add_rules("mode.debug", "mode.release") add_requires("cosmocc") target("test") set_kind("binary") add_files("src/*.c") set_toolchains("@cosmocc") ``` --- --- url: /examples/cpp/mfc.md --- ## MFC Static Library ```lua target("test") add_rules("win.sdk.mfc.static") add_files("src/*.c") ``` ## MFC Shared Library ```lua target("test") add_rules("win.sdk.mfc.shared") add_files("src/*.c") ``` ## MFC Application (Static) ```lua target("test") add_rules("win.sdk.mfc.static_app") add_files("src/*.c") ``` ## MFC Application (Shared) ```lua target("test") add_rules("win.sdk.mfc.shared_app") add_files("src/*.c") ``` --- --- url: /examples/cpp/cppfront.md --- ```lua add_rules("mode.debug", "mode.release") add_requires("cppfront") target("test") add_rules("cppfront") set_kind("binary") add_files("src/*.cpp2") add_packages("cppfront") ``` --- --- url: /examples/cpp/linux-driver-module.md --- In version v2.6.2, xmake fully supports the construction of Linux kernel driver modules. This may be the first and only third-party build tool that supports compiling Linux kernel drivers. ## Hello world module Full example: [Linux Kernel Driver Modules](https://github.com/xmake-io/xmake/tree/master/tests/projects/linux/driver/hello) Its configuration is very simple. You only need to configure the linux-headers package that supports the module, and then apply the `platform.linux.module` build rule. ```lua add_requires("linux-headers", {configs = {driver_modules = true}}) target("hello") add_rules("platform.linux.module") add_files("src/*.c") add_packages("linux-headers") set_license("GPL-2.0") ``` Then directly execute the xmake command, compile with one key, and generate the kernel driver module hello.ko. ```sh $ xmake [20%]: cache compiling.release src/add.c [20%]: cache compiling.release src/hello.c [60%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! ``` We can also look at the complete build command parameters. ```sh $ xmake -v [20%]: cache compiling.release src/add.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr /src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux -headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr /src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\ "hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno -80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect -branch-re gister -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops= 1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone- fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"add\ "-o build/.objs/hello/linux/x86_64/release/src/add.co src/add.c [20%]: cache compiling.release src/hello.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr /src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux -headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr /src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\ "hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno -80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect -branch-re gister -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops= 1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone- fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"hello\ "-o build/.objs/hello/linux/x86_64/release/src/hello.co src/hello.c [60%]: linking.release build/linux/x86_64/release/hello.ko /usr/bin/ld -m elf_x86_64 -r -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/ release/src/add.co build/.objs/hello/linux/x86_64/release/src/hello.co /usr/src/linux-headers-5.11.0-41-generic/scripts/mod/modpost -m -a -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/Module .symvers -e -N -T- WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr /src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux -headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr /src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers- 5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno- mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red- zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno- PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store- data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict- overflow -fno-stack-check -fconserve-stack -o build/.o bjs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko. mod.c /usr/bin/ld -m elf_x86_64 -r --build-id=sha1 -T /usr/src/linux-headers-5.11.0-41-generic/scripts/module.lds -o build/linux/x86_64/ release/hello.ko build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/ release/hello.ko.mod.o ``` Through the `add_requires("linux-headers", {configs = {driver_modules = true}})` configuration package, xmake will automatically find the corresponding linux-headers package from the system first. If it is not found, xmake will also automatically download it, and then automatically configure and build the kernel source code with driver modules, and use it to continue building the kernel module. ## Custom linux-headers path Since the release of v2.6.2, there have been many feedbacks from users. In most cases, the linux kernel driver is built based on a customized version of the linux kernel, so it is necessary to be able to customize the configuration of the linux-headers path instead of using the remote dependency package mode. In fact, we can do this by rewriting the linux-headers package ourselves. ```lua package("linux-headers") on_fetch(function (package, opt) return {includedirs = "/usr/src/linux-headers-5.0/include"} end) package_end() add_requires("linux-headers") target("test") add_rules("platform.linux.module") add_files("src/*.c") add_packages("linux-headers") ``` But this may be a bit cumbersome, so in v2.6.3, we support more convenient setting of the linux-headers path. ```lua target("hello") add_rules("platform.linux.module") add_files("src/*.c") set_values("linux.driver.linux-headers", "/usr/src/linux-headers-5.11.0-41-generic") ``` We can also pass in the linux-headers path as `xmake f --linux-headers=/usr/src/linux-headers` by defining option options. ```lua option("linux-headers", {showmenu = true, description = "Set linux-headers path."}) target("hello") add_rules("platform.linux.module") add_files("src/*.c") set_values("linux.driver.linux-headers", "$(linux-headers)") ``` For more details, please see: [#1923](https://github.com/xmake-io/xmake/issues/1923) ## Cross compilation We also support cross-compilation of kernel driver modules, such as using cross-compilation tool chain on Linux x86\_64 to build Linux Arm/Arm64 driver modules. We only need to prepare our own cross-compilation tool chain, specify its root directory through `--sdk=`, then switch to the `-p cross` platform configuration, and finally specify the architecture arm/arm64 to be built. The cross toolchain used here can be downloaded from here: [Download toolchains](https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/) For more, cross-compilation configuration documents, see: [Configure cross-compilation](/guide/basic-commands/cross-compilation). ::: tip NOTE Currently only supports arm/arm64 cross-compilation architecture, and more platform architectures will be supported in the future. ::: ### Build Arm driver module ```sh $ xmake f -p cross -a arm --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf -c $ xmake -v checking for arm-linux-gnueabihf-g++ ... /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ checking for the linker (ld) ... arm-linux-gnueabihf-g++ checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ ... ok checking for flags (-fPIC) ... ok checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc ... ok checking for flags (-fPIC) ... ok checking for flags (-O2) ... ok checking for ccache ... /usr/bin/ccache [20%]: cache compiling.release src/add.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -c -O2 -std=gnu89 -I/home/ruki/. xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include /generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805 /arch/arm/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated/uapi -I/home/ruki/.xmake /packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/generated/uapi -D__KERNEL__ DMODULE -DKBUILD_MODNAME=\"hello\" -D__LINUX_ARM_ARCH__=6 -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf /bin/../lib/gcc/arm-linux-gnueabihf/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/kconfig .h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar- fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa- cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -mbig- endian -mabi=aapcs-linux -mfpu=vfp -marm -march=armv6k -mtune=arm1136j-s -msoft-float -Uarm -DKBUILD_BASENAME=\"add\" -o build/.objs/hello/cross/arm/ release/src/add.co src/add.c [20%]: cache compiling.release src/hello.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -c -O2 -std=gnu89 -I/home/ruki/. xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/ 7695a30b7add4d3aa4685cbac6815805/arch/arm/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated/uapi -I/home/ruki/. xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -D__LINUX_ARM_ARCH__=6 -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/ 7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/kconfig.h -include /home/ruki/.xmake/packages/l/ linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/compiler_types.h -nostdinc -fno-strict-alia sing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks- fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno- stack-check -fconserve-stack -mbig-endian -mabi=aapcs-linux -mfpu=vfp -marm -march=armv6k -mtune=arm1136j-s -msoft-float -Uarm -DKBUILD_BASENAME=\"hello\" -o build /.objs/hello/cross/arm/release/src/hello.co src/hello.c checking for flags (-MMD -MF) ... ok checking for flags (-fdiagnostics-color=always) ... ok [60%]: linking.release build/cross/arm/release/hello.ko /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-ld -EB -r -o build/.objs/hello/cross/arm/release/build/cross /arm/release/hello.ko.o build/.objs/hello/cross/arm/release/src/add.co build/.objs/hello/cross/arm/release/src/hello.co /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/scripts/mod/modpost -m -a -o build/.objs/hello/cross/arm/release/build/cross/ arm/release/Module.symvers -e -N -T- WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -c -O2 -std=gnu89 -I/home/ruki/. xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include /generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805 /arch/arm/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated/uapi -I/home/ruki/.xmake /packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/generated/uapi -D__KERNEL__ DMODULE -DKBUILD_MODNAME=\"hello\" -D__LINUX_ARM_ARCH__=6 -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf /bin/../lib/gcc/arm-linux-gnueabihf/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/kconfig .h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar- fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa- cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -mbig- endian -mabi=aapcs-linux -mfpu=vfp -marm -march=armv6k -mtune=arm1136j-s -msoft-float -Uarm -o build/.objs/hello/cross/arm/release/build/cross/arm/ release/hello.ko.mod.o build/.objs/hello/cross/arm/release/build/cross/arm/release/hello.ko.mod.c /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-ld -EB --be8 -r --build-id=sha1 -T /home/ruki/. xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/scripts/module.lds -o build/cross/arm/release/hello.ko build/.objs/hello/cross/arm/release/build/cross /arm/release/hello.ko.o build/.objs/hello/cross/arm/release/build/cross/arm/release/hello.ko.mod.o [100%]: build ok! ``` ### Build Arm64 driver module ```sh $ xmake f -p cross -a arm64 --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu -c checking for aarch64-linux-gnu-g++ ... /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ checking for the linker (ld) ... aarch64-linux-gnu-g++ checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ ... ok checking for flags (-fPIC) ... ok checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc ... ok checking for flags (-fPIC) ... ok checking for flags (-O2) ... ok checking for ccache ... /usr/bin/ccache [20%]: cache compiling.release src/add.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -c -O2 -std=gnu89 -I/home/ruki/. xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include /generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de /arch/arm64/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated/uapi -I/home/ruki/.xmake /packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/generated/uapi -D__KERNEL__- DMODULE -DKBUILD_MODNAME=\"hello\" -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch 64-linux-gnu/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/kconfig.h -include /home/ruki/. xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1- falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining- fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"add\" -o build/. objs/hello/cross/arm64/release/src/add.co src/add.c [20%]: cache compiling.release src/hello.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -c -O2 -std=gnu89 -I/home/ruki/. xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include /generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de /arch/arm64/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated/uapi -I/home/ruki/.xmake /packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/generated/uapi -D__KERNEL__- DMODULE -DKBUILD_MODNAME=\"hello\" -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch 64-linux-gnu/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/kconfig.h -include /home/ruki/. xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1- falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining- fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"hello\" -o build/. objs/hello/cross/arm64/release/src/hello.co src/hello.c checking for flags (-MMD -MF) ... ok checking for flags (-fdiagnostics-color=always) ... ok [60%]: linking.release build/cross/arm64/release/hello.ko /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ld -EL -maarch64elf -r -o build/.objs/hello/cross/arm64/release/build /cross/arm64/release/hello.ko.o build/.objs/hello/cross/arm64/release/src/add.co build/.objs/hello/cross/arm64/release/src/hello.co /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/scripts/mod/modpost -m -a -o build/.objs/hello/cross/arm64/release/build/cross/ arm64/release/Module.symvers -e -N -T- WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -c -O2 -std=gnu89 -I/home/ruki/. xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include /generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated/uapi -I /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include /generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux- gnu/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/kconfig.h -include /home/ruki/.xmake/packages/ l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops= 1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno- ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack- o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/hello.ko.mod.o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/ hello.ko.mod.c /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ld -EL -maarch64elf -r --build-id=sha1 -T /home/ruki/.xmake /packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/scripts/module.lds -o build/cross/arm64/release/hello.ko build/.objs/hello/cross/arm64/release/build/cross/ arm64/release/hello.ko.o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/hello.ko.mod.o [100%]: build ok! ``` --- --- url: /examples/cpp/linux-bpf.md --- Since 2.5.3, it supports bpf program construction, supports both linux and android platforms, and can automatically pull llvm and android ndk toolchains. For more details, please see: [#1274](https://github.com/xmake-io/xmake/issues/1274) ```lua add_rules("mode.release", "mode.debug") add_rules("platform.linux.bpf") add_requires("linux-tools", {configs = {bpftool = true}}) add_requires("libbpf") if is_plat("android") then add_requires("ndk >=22.x") set_toolchains("@ndk", {sdkver = "23"}) else add_requires("llvm >=10.x") set_toolchains("@llvm") add_requires("linux-headers") end target("minimal") set_kind("binary") add_files("src/*.c") add_packages("linux-tools", "linux-headers", "libbpf") set_license("GPL-2.0") ``` --- --- url: /examples/cpp/merge-static-libraries.md --- ## Automatically merge target libraries After 2.5.8, we can enable automatic merging of all dependent static libraries by setting the `build.merge_archive` strategy, for example: ```lua add_rules("mode.debug", "mode.release") target("add") set_kind("static") add_files("src/add.c") add_files("src/subdir/add.c") target("sub") set_kind("static") add_files("src/sub.c") add_files("src/subdir/sub.c") target("mul") set_kind("static") add_deps("add", "sub") add_files("src/mul.c") set_policy("build.merge_archive", true) ``` The mul static library automatically merges the add and sub static libraries to generate a complete libmul.a library containing add/sub code. This merge is relatively stable and complete, supports ar and msvc/lib.exe, also supports the merge of static libraries generated by the cross-compilation tool chain, and also supports static libraries with the same name obj file. ## Merge specified static library files If the automatic merge does not meet the requirements, we can also actively call the `utils.archive.merge_archive` module to merge the specified static library list in the `after_link` stage. ```lua target("test") after_link(function (target) import("utils.archive.merge_staticlib") merge_staticlib(target, "libout.a", {"libfoo.a", "libbar.a"}) end) ``` ## Use add\_files to merge static libraries In fact, our previous version already supports merging static libraries through `add_files("*.a")`. ```lua target("test") set_kind("binary") add_files("*.a") add_files("*.c") ``` Related issues: [#1638](https://github.com/xmake-io/xmake/issues/1638) --- --- url: /examples/cpp/openmp.md --- After v2.6.1, the configuration of openmp has been improved, which is more simplified and unified. We no longer need to configure additional rules. The same effect can be achieved only through a common openmp package. ```lua add_requires("openmp") target("loop") set_kind("binary") add_files("src/*.cpp") add_packages("openmp") ``` Before v2.5.9 ```lua add_requires("libomp", {optional = true}) target("loop") set_kind("binary") add_files("src/*.cpp") add_rules("c++.openmp") add_packages("libomp") ``` If it is c code, you need to enable ʻadd\_rules("c.openmp")\`. If it is c/c++ mixed compilation, then these two rules must be set. --- --- url: /examples/cpp/protobuf.md --- ## Using c library ```lua add_requires("protobuf-c") target("console_c")      set_kind("binary")      add_packages("protobuf-c") add_rules("protobuf.c")      add_files("src/*.c")      add_files("src/*.proto") ``` We can also set `proto_public = true` to export the proto's header search directory and make it available for other parent targets to inherit from. ```lua add_packages("protobuf-c", {public = true}) add_files("src/**.proto", {proto_public = true}) ``` ::: tip NOTE Since the headers generated by protobuf reference the headers of the protobuf-c package, we also need to mark the package headers as `{public = true}` to export it. ::: ## Using the C++ library ```lua add_requires("protobuf-cpp") target("console_c++")      set_kind("binary")      set_languages("c++11")      add_packages("protobuf-cpp") add_rules("protobuf.cpp")      add_files("src/*.cpp")      add_files("src/*.proto") ``` We can also set `proto_public = true` to export the proto's header search directory and make it available for other parent targets to inherit from. ```lua add_packages("protobuf-cpp", {public = true}) add_files("src/**.proto", {proto_public = true}) ``` ::: tip NOTE Since the headers generated by protobuf reference the headers of the protobuf-cpp package, we also need to mark the package headers as `{public = true}` to export it. ::: --- --- url: /examples/cpp/qt.md --- Create an empty project: ```sh $ xmake create -t qt.console test $ xmake create -t qt.static test $ xmake create -t qt.shared test $ xmake create -t qt.quickapp test $ xmake create -t qt.widgetapp test ``` For more project templates see: `xmake create --help` xmake will detect Qt SDK automatically and we can also set the SDK directory manually. ```sh $ xmake f --qt=~/Qt/Qt5.9.1 ``` The MingW SDK specified above uses the environment that comes with the Tools directory under Qt. Of course, if there are other third-party MingW compilation environments, they can also be specified manually. For details, please refer to: [MingW Configuration](/guide/basic-commands/build-configuration#mingw). For more details, please refer to: [#160](https://github.com/xmake-io/xmake/issues/160) In addition, currently xmake also supports Qt/Wasm. For details, see: [Wasm Configuration](/guide/basic-commands/build-configuration#wasm-webassembly) ```sh $ xmake f -p wasm ``` ## 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.quickapp") add_files("src/*.cpp") add_files("src/qml.qrc") ``` ::: tip NOTE If you are using your own compiled static version of the QT SDK, you need to switch to the `add_rules("qt.quickapp_static")` static rule, because the linked libraries are different and need to be statically linked. ::: Next, we try to compile, usually, if you use the Qt installation package to install by default, and do not modify the installation path, then in most cases you can automatically detect the root path of the QT SDK, for example: ```sh $ xmake checking for the architecture ... x86_64 checking for the Xcode directory ... /Applications/Xcode.app checking for the SDK version of Xcode ... 10.15 checking for the Qt SDK directory ... /Users/ruki/Qt5.13.2/5.13.2/clang_64 checking for the Qt SDK version ... 5.13.2 [0%]: cache compiling.release src/main.cpp [49%]: compiling.qt.qrc src/qml.qrc [100%]: linking.release test Build ok! ``` Then we continue to run it: ```sh $ xmake run ``` The effect is as follows: ![](/assets/img/guide/qt_quickapp.png) ## Quick Plugin For a full example see: [quickplugin example](https://github.com/xmake-io/xmake/tree/master/tests/projects/qt/quickplugin) ```lua add_rules("mode.debug", "mode.release") target("demo") add_rules("qt.qmlplugin") add_headerfiles("src/*.h") add_files("src/*.cpp") set_values("qt.qmlplugin.import_name", "My.Plugin") ``` ## Widgets Application ```lua target("qt_widgetapp") add_rules("qt.widgetapp") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/mainwindow.ui") -- add files with Q_OBJECT meta (only for qt.moc) add_files("src/mainwindow.h") ``` ::: tip NOTE If you are using your own compiled static version of the QT SDK, you need to switch to the `add_rules("qt.widgetapp_static")` static rule, because the linked libraries are different and need to be statically linked. ::: The effect is as follows: ![](/assets/img/guide/qt_widgetapp.png) ## 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. ```sh $ 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%]: cache compiling.release src/main.cpp [100%]: linking.release libappdemo.so [100%]: generating.qt.app appdemo.apk ``` Then install to the device: ```sh $ xmake install installing appdemo ... installing build/android/release/appdemo.apk .. success install ok!👌 ``` ## Supported Qt SDKs ### The official Qt SDK installation package This is usually detected automatically on macos/windows, but it is possible to specify the Qt SDK path manually. ```sh $ xmake f --qt=[qt sdk path] ``` ### The Ubuntu Apt package After installing the Qt SDK using apt, xmake will also be able to detect it automatically. ```sh $ sudo apt install -y qtcreator qtbase5-dev $ xmake ``` ### Qt Mingw SDK from msys2/pacman xmake also supports the Qt Mingw SDK installed from pacman ```sh $ pacman -S mingw-w64-x86_64-qt5 mingw-w64-x86_64-qt-creator $ xmake ``` ### Qt SDK package from aqtinstall script The Qt SDK installed by [aqtinstall](https://github.com/miurahr/aqtinstall) is based entirely on the official SDK structure and is therefore fully supported by xmake. However, it is usually necessary to specify the SDK path yourself. ```sh $ xmake f --qt=[Qt SDK] ``` ### Cross-Platform Qt Builds For cross-platform Qt development, xmake supports using separate SDKs for host tools and the target platform. This is particularly useful when building Qt applications for a different platform than your development machine. The `--qt_host` option allows you to specify the location of Qt tools that are compatible with your build machine, while `--qt` points to the SDK for the target platform: ```sh $ xmake f --qt=[target Qt sdk] --qt_host=[host Qt sdk] ``` ::: tip NOTE * Make sure the host and target Qt versions match, or it may cause build issues. * Native deployment tools like `windeployqt` and `macdeployqt` must run on their respective platforms, so cross-platform tasks such as `xmake install` may fail. ::: ### Qt packages from the xmake-repo repository xmake now officially provides a variety of modules for both the Qt5 and Qt6 SDKs that can be integrated automatically without any manual installation. Just configure the integration packages and xmake will automatically handle the Qt installation and integration and compile the project automatically. #### Qt5 Example ```lua add_rules("mode.debug", "mode.release") add_requires("qt5widgets") target("test") add_rules("qt.widgetapp") add_packages("qt5widgets") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/mainwindow.ui") -- add files with Q_OBJECT meta (only for qt.moc) add_files("src/mainwindow.h") ``` #### Qt6 Example ```lua add_rules("mode.debug", "mode.release") add_requires("qt6widgets") target("test") add_rules("qt.widgetapp") add_packages("qt6widgets") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/mainwindow.ui") -- add files with Q_OBJECT meta (only for qt.moc) add_files("src/mainwindow.h") ``` In addition to the `qt6widgets` package, the repository also provides `qt6gui`, `qt6network` and other Qt6 packages that can be used. Once configured, simply execute: ```sh $ xmake ``` ### Qt packages from vcpkg/conan There is no time to support it yet, so please try to integrate the Qt SDK in the same way as above. --- --- url: /examples/cpp/wasm.md --- All c/c++ programs can be compiled to Wasm without any xmake.lua configuration changes, just switch to the wasm compilation platform and compile. ```sh $ xmake f -p wasm $ xmake ``` For detailed wasm compilation configuration see: [wasm configuration](/guide/basic-commands/build-configuration#wasm-webassembly) Alternatively, when compiling a file with the `-preload-file assets/xxx.md` setting, we can also simplify its setup by configuring ```lua target("test5") set_kind("binary") add_files("src/*.cpp") add_values("wasm.preloadfiles", "src/xxx.md") add_values("wasm.preloadfiles", "src/xxx2.md") ``` --- --- url: /examples/cpp/winsdk.md --- ```lua target("usbview") add_rules("win.sdk.application") add_files("*.c", "*.rc") add_files("xmlhelper.cpp", {rules = "win.sdk.dotnet"}) ``` If you want to known more information, you can see [#173](https://github.com/xmake-io/xmake/issues/173). --- --- url: /examples/cpp/wdk.md --- Xmake will detect WDK automatically and we can also set the WDK directory manually. ```sh $ 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). And see [WDK examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/windows/driver) ## 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", {rules = "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", {rules = "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. ```sh $ 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") set_values("wdk.sign.digest_algorithm", "sha256") ``` 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") set_values("wdk.sign.digest_algorithm", "sha256") ``` 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)") set_values("wdk.sign.digest_algorithm", "sha256") ``` ### 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")) set_values("wdk.sign.digest_algorithm", "sha256") ``` ## 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: ```sh $ xmake f --wdk_winver=[win10_rs3|win8|win7|win7_sp1] $ xmake ``` --- --- url: /examples/embed/keil-mdk.md --- Related example project: [Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/embed/mdk/hello) xmake will automatically detect the compiler installed by Keil/MDK, related issues [#1753](https://github.com/xmake-io/xmake/issues/1753). Compile with armcc ```sh $ xmake f -p cross -a cortex-m3 --toolchain=armcc -c $ xmake ``` Compile with armclang ```sh $ xmake f -p cross -a cortex-m3 --toolchain=armclang -c $ xmake ``` ### Executable program ```lua target("hello") add_deps("foo") add_rules("mdk.console") add_files("src/*.c", "src/*.s") add_includedirs("src/lib/cmsis") set_runtimes("microlib") ``` It should be noted that when some mdk programs all use the microlib library to run, it requires the compiler to add the `__MICROLIB` macro definition, and the linker to add various configurations such as `--library_type=microlib`. We can set directly to the microlib runtime library through `set_runtimes("microlib")`, and all relevant options can be set automatically. ### Static library program ```lua add_rules("mode.debug", "mode.release") target("foo") add_rules("mdk.static") add_files("src/foo/*.c") set_runtimes("microlib") ``` --- --- url: /examples/embed/keil-c51.md --- ## Executable programs ```lua target("hello") add_rules("c51.binary") set_toolchains("c51") add_files("src/main.c") ``` --- --- url: /examples/other-languages/dlang.md --- Create an empty project: ```sh $ xmake create -l dlang -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.d") ``` Starting from the v2.3.6 version, xmake adds support for dub package management, which can quickly integrate third-party dependency packages of dlang: ```lua add_rules("mode.debug", "mode.release") add_requires("dub::log 0.4.3", {alias = "log"}) add_requires("dub::dateparser", {alias = "dateparser"}) add_requires("dub::emsi_containers", {alias = "emsi_containers"}) add_requires("dub::stdx-allocator", {alias = "stdx-allocator"}) add_requires("dub::mir-core", {alias = "mir-core"}) target("test") set_kind("binary") add_files("src/*.d") add_packages("log", "dateparser", "emsi_containers", "stdx-allocator", "mir-core") ``` However, there are still some imperfections. For example, all cascading dependency packages must be manually configured at present, which will be a bit more cumbersome and needs to be improved in the future. For more examples, see: [Dlang Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/dlang) --- --- url: /examples/other-languages/cuda.md --- Create an empty project: ```sh $ 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") ``` ::: tip NOTE Starting with v2.2.7, the default build will enable device-link. (see [Separate Compilation and Linking of CUDA C++ Device Code](https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/)) If you want to disable device-link, you can set it with `set_policy("build.cuda.devlink", false)`. ::: ::: tip NOTE Device functions in cuda source files should be device-linked once and only once. On targets with kind `binary` or `shared` xmake will automatically perform the device-link which takes the static libraries they depend into account, while for `static` targets by default will not be device-linked. However, if the final `binary` or `shared` target do not contain any cuda files, the device-link stage could be missing, resulting in an undefined reference error. In this case the static target should be set `add_values("cuda.build.devlink", true)` manually. ::: Xmake will detect Cuda SDK automatically and we can also set the SDK directory (or SDK version for default installations) manually. ```sh $ xmake f --cuda=/usr/local/cuda-9.1/ $ xmake f --cuda=9.1 $ xmake ``` If you want to known more information, you can see [#158](https://github.com/xmake-io/xmake/issues/158). --- --- url: /examples/other-languages/fortran.md --- After v2.3.6, the gfortran compiler is supported to compile fortran projects. We can quickly create an empty project based on fortran by using the following command: After v2.3.8, xmake also supports Intel Fortran Compiler, you only need to switch the toolchain: `xmake f --toolchain=ifort` ```sh $ xmake create -l fortran -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.f90") ``` More code examples can be viewed here: [Fortran Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/fortran) --- --- url: /examples/other-languages/golang.md --- Xmake also supports the construction of go programs, and also provides command support for creating empty projects: ```sh $ xmake create -l go -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.go") ``` In v2.3.6 version, xmake has made some improvements to its build support, and also supports cross compilation of go. For example, we can compile windows programs on macOS and linux: ```sh $ xmake f -p windows -a x86 ``` In addition, the new version also initially supports the third-party dependency package management of go: ```lua add_rules("mode.debug", "mode.release") add_requires("go::github.com/sirupsen/logrus", {alias = "logrus"}) add_requires("go::golang.org/x/sys/internal/unsafeheader", {alias = "unsafeheader"}) if is_plat("windows") then add_requires("go::golang.org/x/sys/windows", {alias = "syshost"}) else add_requires("go::golang.org/x/sys/unix", {alias = "syshost"}) end target("test") set_kind("binary") add_files("src/*.go") add_packages("logrus", "syshost", "unsafeheader") ``` However, there are still some imperfections. For example, all cascading dependency packages must be manually configured at present, which will be a bit more cumbersome and needs to be improved in the future. For more examples, see: [Go Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/go) --- --- url: /examples/other-languages/nim.md --- After v2.5.9, we have added support for the Nimlang project. For related issues, see: [#1756](https://github.com/xmake-io/xmake/issues/1756) ## Create an empty project We can use the `xmake create` command to create an empty project. ```sh xmake create -l nim -t console test xmake create -l nim -t static test xmake create -l nim -t shared test ``` ## Console Program ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/main.nim") ``` ```sh $ xmake -v [33%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache -o:b uild/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` ## Static library program ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```sh $ xmake -v [33%]: linking.release libfoo.a /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :staticlib --noMain --passC:-DNimMain=NimMain_B6D5BD02 --passC:-DNimMainInner=NimMainInner_B6D5B D02 --passC:-DNimMainModule=NimMainModule_B6D5BD02 --passC:-DPreMain=PreMain_B6D5BD02 --passC:-D PreMainInner=PreMainInner_B6D5BD02 -o:build/macosx/x86_64/release/libfoo.a src/foo.nim [66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` ## Dynamic library program ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```sh $ xmake -rv [33%]: linking.release libfoo.dylib /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :lib --noMain -o:build/macosx/x86_64/release/libfoo.dylib src/foo.nim [66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` ## C code mixed compilation ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/*.c") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ## Nimble dependency package integration For a complete example, see: [Nimble Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/nimble_package) ```lua add_rules("mode.debug", "mode.release") add_requires("nimble::zip >0.3") target("test") set_kind("binary") add_files("src/main.nim") add_packages("nimble::zip") ``` ```nim [main.nim] import zip/zlib echo zlibVersion() ``` ## Native dependency package integration For a complete example, see: [Native Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/native_package) ```lua add_rules("mode.debug", "mode.release") add_requires("zlib") target("test") set_kind("binary") add_files("src/main.nim") add_packages("zlib") ``` main.nim ```nim proc zlibVersion(): cstring {.cdecl, importc} echo zlibVersion() ``` --- --- url: /examples/other-languages/lex-yacc.md --- ```lua target("calc")      set_kind("binary")      add_rules("lex", "yacc")      add_files("src/*.l", "src/*.y") ``` --- --- url: /examples/other-languages/pascal.md --- After 2.5.8, we can support the construction of Pascal programs. For related issues, see: [#388](https://github.com/xmake-io/xmake/issues/388) ## Console Program ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.pas") ``` ## Dynamic library program ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.pas") target("test") set_kind("binary") add_deps("foo") add_files("src/main.pas") ``` More examples: [Pascal examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/pascal) --- --- url: /examples/other-languages/objc.md --- ## Console Create an empty project: ```sh $ xmake create -l objc -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.m") ``` For more examples, see: [Objc Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/objc++) ## Application Generate *.app/*.ipa application and supports iOS/MacOS. ```lua target("test") add_rules("xcode.application") add_files("src/*.m", "src/**.storyboard", "src/*.xcassets") add_files("src/Info.plist") ``` ::: tip NOTE After 2.5.7, you can directly add `*.metal` files, xmake will automatically generate default.metallib for the application to load and use. ::: ### Create Project We can also quickly create project through template: ```sh $ xmake create -t xcode.macapp -l objc test $ xmake create -t xcode.iosapp -l objc test ``` ### Build Program ```sh $ xmake f -p [iphoneos|macosx] $ xmake [ 18%]: compiling.xcode.release src/Assets.xcassets [ 27%]: processing.xcode.release src/Info.plist [ 72%]: compiling.xcode.release src/Base.lproj/Main.storyboard [ 81%]: compiling.xcode.release src/Base.lproj/LaunchScreen.storyboard [ 45%]: cache compiling.release src/ViewController.m [ 63%]: cache compiling.release src/AppDelegate.m [ 54%]: cache compiling.release src/SceneDelegate.m [ 36%]: cache compiling.release src/main.m [ 90%]: linking.release test [100%]: generating.xcode.release test.app [100%]: build ok! ``` ### Codesign For iOS programs, it will detect that the system first signs the app with available signatures. Of course, we can also manually specify other signature certificates: ```sh $ xmake f -p iphoneos --xcode_codesign_identity='Apple Development: xxx@gmail.com (T3NA4MRVPU)' --xcode_mobile_provision='iOS Team Provisioning Profile: org.tboox.test --xcode_bundle_identifier=org.tboox.test' $ xmake ``` If it is cumbersome to configure the signature every time, you can set it to the `xmake global` global configuration, or you can set it separately for each target in xmake.lua: ```lua target("test") add_rules("xcode.application") add_files("src/*.m", "src/**.storyboard", "src/*.xcassets") add_files("src/Info.plist") add_values("xcode.bundle_identifier", "org.tboox.test") add_values("xcode.codesign_identity", "Apple Development: xxx@gmail.com (T3NA4MRVPU)") add_values("xcode.mobile_provision", "iOS Team Provisioning Profile: org.tboox.test") ``` How do we know the signature configuration we need? One is to view it in xcode. In addition, xmake also provides some auxiliary tools to dump all currently available signature configurations: ```sh $ xmake l private.tools.codesign.dump ==================================== codesign identities ==================================== { "Apple Development: waruqi@gmail.com (T3NA4MRVPU)" = "AF73C231A0C35335B72761BD3759694739D34EB1" } ===================================== mobile provisions ===================================== { "iOS Team Provisioning Profile: org.tboox.test" = " AppIDName XC org tboox test ApplicationIdentifierPrefix 43AAQM58X3 ... ``` We also provide other auxiliary tools to re-sign existing ipa / app programs, for example: ```sh $ xmake l utils.ipa.resign test.ipa | test.app [codesign_identity] [mobile_provision] [bundle_identifier] ``` Among them, the following signature parameters are optional, if not set, then a valid signature will be detected by default: ```sh $ xmake l utils.ipa.resign test.ipa $ xmake l utils.ipa.resign test.app "Apple Development: waruqi@gmail.com (T3NA4MRVPU)" $ xmake l utils.ipa.resign test.ipa "Apple Development: waruqi@gmail.com (T3NA4MRVPU)" iOS Team Provisioning Profile: org.tboox.test" org.tboox.test ``` ### Run the application Currently only supports running macos program: ```sh $ xmake run ``` The effect is as follows: ![](/assets/img/guide/macapp.png) ### Package program If it is an iOS program, it will generate an ipa installation package, if it is macOS, it will generate a dmg package (dmg package generation is still under development for the time being). ```sh $ xmake package output: build/iphoneos/release/arm64/test.ipa package ok! ``` We also provide auxiliary tools to package the specified app program: ```sh $ xmake l utils.ipa.package test.app output.ipa [iconfile.png] ``` ### Install If it is an iOS program, it will install ipa to the device, if it is macos, it will install the app to the `/Applications` directory. ```sh $ xmake install ``` We also provide auxiliary tools to install the specified ipa/app program to the device: ```sh $ xmake l utils.ipa.install test.app $ xmake l utils.ipa.install test.ipa ``` ### Uninstall ::: tip NOTE Currently only the macos program is supported ::: ```sh $ xmake uninstall ``` ## Framework Program ```lua target("test") add_rules("xcode.framework") add_files("src/*.m") add_files("src/Info.plist") ``` We can also quickly create project through template: ```sh $ xmake create -t xcode.framework -l objc test ``` In addition, xmake v2.3.9 and above, xmake also provides a complete iosapp/macapp empty project template with framework library usage, you can fully experience framework compilation, dependent use and integration into app applications. At the same time, if we turn on the emulator, xmake can support directly `xmake install` and `xmake run` to install the app to the emulator and load and run it. ```sh $ xmake create -t xcode.iosapp_with_framework -l objc testapp $ cd testapp $ xmake f -p iphoneos -a x86_64 $ xmake $ xmake install $ xmake run ``` ## Bundle Program ```lua target("test") add_rules("xcode.bundle") add_files("src/*.m") add_files("src/Info.plist") ``` We can also quickly create project through template: ```sh $ xmake create -t xcode.bundle -l objc test ``` --- --- url: /examples/other-languages/swift.md --- Create an empty project: ```sh $ xmake create -l swift -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.swift") ``` For more examples, see: [Swift Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/swift) --- --- url: /examples/other-languages/vala.md --- After 2.5.7 to support the construction of Vala programs, we need to apply the `add_rules("vala")` rule, and the glib package is necessary. related issues: [#1618](https://github.com/xmake-io/xmake/issues/1618) `add_values("vala.packages")` is used to tell valac which packages the project needs, it will introduce the vala api of the relevant package, but the dependency integration of the package still needs to be downloaded and integrated through `add_requires("lua")`. ## Console program ```lua add_rules("mode.release", "mode.debug") add_requires("lua", "glib") target("test") set_kind("binary") add_rules("vala") add_files("src/*.vala") add_packages("lua", "glib") add_values("vala.packages", "lua") ``` ## Static library program After v2.5.8, we continue to support the construction of library programs. The exported interface header file name can be set through `add_values("vala.header", "mymath.h")`, and through `add_values("vala.vapi", "mymath -1.0.vapi")` Set the name of the exported vapi file. ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("static") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` ## Dynamic library program ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("shared") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` More examples: [Vala examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/vala) --- --- url: /examples/other-languages/zig.md --- Create an empty project: ```sh $ xmake create -l zig -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.zig") ``` For more examples, see: [Zig Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/zig) --- --- url: /zh/about/contact.md --- * 邮箱: * 主页:[xmake.io](https://xmake.io/zh/) * 社区 * [Reddit论坛](https://www.reddit.com/r/xmake/) * [Telegram群组](https://t.me/tbooxorg) * [Discord聊天室](https://discord.gg/xmake) * QQ群:343118190, 662147501 * 源码:[Github](https://github.com/xmake-io/xmake), [Gitee](https://gitee.com/tboox/xmake) * 微信公众号:tboox-os --- --- url: /zh/about/course.md --- ## 实验楼课程 [Xmake 带你轻松构建 C/C++ 项目](https://www.lanqiao.cn/courses/2764) 是我们在实验楼上推出的一门 xmake 入门和进阶课程(收费),通过边学边做实验的方式,帮助你快速学习 xmake 的使用。 通过此处优惠码购买可享 9 折优惠:`NYFbmf3X` ::: tip 注意 !!!本课程不是视频课程,而是线上环境让用户一边操作一边学习,前两节课可以免费试学体验。购买前请务必仔细确认是否满足自己的需求!!! ::: ### 课程介绍 本课程以循序渐进的方式,带你入门和进阶 xmake,从最基础的编译配置,到复杂项目的定制化组织和维护,在课程最后几节,我们还通过实战的方式,一步步带你体验第三方 C/C++ 项目的移植编译,以及 vscode/xmake 集成环境的可视化编译操作流程。最后一节实验中,我们还会讲解如何使用 xmake 和 vscode 去编译开发基于 Qt 的应用程序。 ### 你将学到 | | | | --- | --- | | C/C++ 程序的编译运行和调试 | xmake 基础命令使用 | | C/C++ 依赖库集成和使用 | 大型工程结构的维护 | | xmake 复杂脚本和规则的编写 | 如何实现跨平台编译 | | xmake 插件开发 | VS Code/xmake 集成环境的使用 | | xmake 的基础语法 | C/C++ 项目基础编译配置 | | 多个目标程序的依赖编译 | 预编译头文件的设置 | | cmake/makefile 工程文件的生成 | xmake 脚本运行插件的使用 | | C/C++ 代码的移植编译 | Qt 项目程序的编译开发 | ### 课程链接 链接地址: 你也可以通过扫描下方二维码进入课程: ![](/assets/img/xmake_course.png) ## 视频课程 后期作者如果有时间,也会考虑推出视频课程,敬请期待。 --- --- url: /zh/about/peripheral_items.md --- ## 周边物品 这里会不定期推出一些 xmake 相关的限量版周边纪念品。 考虑到成本问题,作者目前还没有能力免费提供,因此所有周边物品仍需收取一定费用,但基本上都是成本价+运费,也会有一些微薄的收益用于支持项目的持续发展。 有需要的同学可以微信联系(waruqi)进行预定。 付款方式可参考:[赞助](/zh/about/sponsor),或直接微信转账。 ### 限量版金边马克杯(带盖勺🥄) 价格:¥68(包邮) 实物展示: --- --- url: /zh/about/who_is_using_xmake.md --- 如果您正在使用 xmake,欢迎点击编辑 [此页面](https://github.com/xmake-io/xmake-docs/edit/master/docs/zh/about/who_is_using_xmake.md),通过 PR 将信息提交至下方列表,让更多用户了解有多少人在使用 xmake,也能让用户更加安心地使用 xmake。 我们也会有更多动力持续投入,让 xmake 项目和社区更加繁荣。 ::: tip 注意 如果您不方便添加您的项目地址,可以简单描述项目的使用场景。 ::: ## 用户列表 {#user-list} | 用户 (公司名/个人联系方式) | 项目 (项目简介/项目地址) | 评论 (可选) | | :--- | :--- | :--- | | [ruki](https://github.com/waruqi) | [tbox](https://github.com/tboox/tbox) & [ltui](https://github.com/tboox/ltui) | ... | | [zsx](https://github.com/acl-dev/acl) | [acl](https://github.com/acl-dev/acl) | Good | | [深圳市云语科技](https://www.raysync.cn/) | [镭速-大文件传输系统](https://www.raysync.cn/) | 非常有特点的构建系统 | | [深圳金鼎威视](http://www.gzgve.com/) | 暂时都是内部项目 | 嵌入式开发中目前用过的最优秀的c程序构建工具 | | [WillyJL](https://github.com/Willy-JL) | [CP77 Discord RPC](https://github.com/Willy-JL/cp77-discord-rpc) ([Homepage](https://www.nexusmods.com/cyberpunk2077/mods/986)) | Perfect and easy way to get into C++ without prior experience | | [SirLynix](https://github.com/SirLynix) | [BurgWar](https://github.com/DigitalPulseSoftware/BurgWar) (Video game) & [obs-kinect](https://github.com/SirLynix/obs-kinect) (plugin for OBS) | After programming for more than 12 years in C++, I can say xmake is a life changer. It's to C++ what Cargo is to Rust, a truly helpful tool (especially with external dependencies) you won't want to give up. | [yamashi](https://github.com/yamashi) | [Cyber Engine Tweaks](https://github.com/yamashi/CyberEngineTweaks) (Mod for Cyberpunk 2077) | Dll injection to add Lua scripting, imgui user interface, console cheats and other hacks to Cyberpunk 2077. | [Tilted Phoques](https://github.com/tiltedphoques) | [Skyrim/Fallout Together](https://github.com/tiltedphoques/TiltedOnline/) (Mod for Skyrim & Fallout 4) | Dll injection to add multiplayer to CreationKit games such as Skyrim and Fallout 4. | [武汉凌久微电子有限公司](http://www.ljmicro.cn) | 内部嵌入式图形驱动项目 | 支持翼辉、天脉、锐华等多种国产嵌入式实时操作系统的GP100/200系列国产GPU OpenGL/ES驱动 | | [yecate](https://github.com/yecate) | 个人项目 | 跨平台构建的利器,xmake在我日常开发工作中提供了很大的便利 | | [fasiondog](https://github.com/fasiondog) | [Hikyuu Quant Framework](https://github.com/fasiondog/hikyuu) 基于C++/Python的开源量化交易研究框架 | xmake 的简洁高效是C/C++跨平台工程的解放 | | [KnightJun](https://github.com/knightjun) | [BingW](https://github.com/knightjun/BingW)(bing wallpaper) | xmake is an excellent, simple, easy-to-use and powerful build tool. | [Phate6660](https://github.com/Phate6660) | [cppfetch](https://github.com/Phate6660/cppfetch) (info fetch tool) | I tried this out after seeing [this](https://github.com/xmake-io/xmake/wiki/C-and-Cplusplus-build-system,-I-use-xmake), and after using it I loved it enough that I replaced meson with this! | walletiger | ERTC - an easy and efficient cross platform RTC project | 个人项目,简单易用跨平台 兼容 webrtc 标准的 SFU及前端通信组件 , xmake + tbox 提供了优秀的跨平台编译和底层库支持| | [Alvin](https://github.com/idealvin) | [co](https://github.com/idealvin/co) | A go-style coroutine library in C++11. Xmake is nice! | | [ekko](https://github.com/ekkone) | 电网融合终端linux应用,个人使用 | 相比Makefile和Cmake简洁易懂太多了,构建十分高效,并发编译速度比公司用的cmake快一个数量级,希望发展越来越好 | | [合宙通信](https://www.openluat.com/) | https://gitee.com/openLuat/luatos-soc-air101 | xmake 是非常简洁,高效,方便的C/C++构建工具,节省很多时间 | | [Akselmo](https://codeberg.org/akselmo) | [Artificial Rage](https://codeberg.org/akselmo/artificial-rage), a simple FPS game and (probably) an FPS game engine in future | Switched from cmake to xmake. The ease of use is great, and being able to generate cmake files for tools that require it is a cherry on top. Highly recommend! | [JackLan](https://github.com/lanjackg2003) | 目前,尝试在个人嵌入式项目中使用 | 在几年前,使用make与makefile实现了与XMake类似的C/C++项目构建功能。现在回想起来,从零到有,整个流程也并非那么一帆风顺。最近想使用CMake重新实现这套构建,无意中发现了XMake。让我眼前一亮的是,他使用LUA语法(早些年从合宙的GSM模块玩起来),容易上手。我将之前的Make项目移植使用XMake工具,也就是几小时,这个过程也只是我从第一次参考官方说明做起。所以确实省时省力。希望XMake能够让更多的开发者知道与使用,成为更加主流的构建工具! | [XmacsLabs](https://github.com/XmacsLabs) | [mogan](https://github.com/XmacsLabs/mogan) | autotool和CMake开发环境不易配置,vcpkg等CMake包管理器存在不少兼容性问题,极度影响开发体验,导致墨者实验室失去了一大批潜在开发者。xmake为老旧软件的维护提供了紧凑易用的工具,有效简化了添加新类库的流程,减少了无谓的工作量。 | [Dozingfiretruck](https://github.com/Dozingfiretruck) | C语言实现的NES模拟器 GitHub地址: https://github.com/Dozingfiretruck Gitee地址: https://gitee.com/Dozingfiretruck/nes | xmake 可以使你极其方便的部署C/C++构建,节省大量时间从而将精力放在代码编写上而非项目构建上 | | [WSSDude](https://github.com/WSSDude) | [Hitman Audio Tool](https://github.com/WSSDude/HitmanAudioTool) | Very easy to use build system with what I believe is currently the best package management integrated within. Definitely plan to include it in my other future projects (both personal and public). Using it since 2020 and counting.| | Meidozuki | [VBAO](https://github.com/Meidozuki/VBAO) | 个人开发项目,用于构建MVVM开发框架的utility library,提供C++和python接口。xmake的语法相比cmake十分简洁,尤其是处理包依赖问题时:cmake的find\_package(尤其是Config Mode)依赖于第三方库作者的水平,xmake接入了Conan包管理,大大简化了这一过程;对于Makefile-only的项目,cmake需要额外了解IMPORTED library target的概念,十分别扭,xmake的写法就很舒服。虽然xmake的命令行已经足够简洁了,不过还是期待主流IDE对于xmake更好的支持 | | Luisa-Group | [Luisa-Compute](https://luisa-render.com/) | 跨平台高性能通用流处理器计算框架。凭借xmake良好的跨平台能力和lua DSL强大的表达能力,极大提升了项目开发效率和编译体验。| | [Sunrisepeak](https://github.com/Sunrisepeak) | [DStruct](https://github.com/Sunrisepeak/DStruct) (an easy-to-port/learn/use C++ data structure template library) | DStruct 是一个平台无关(易于移植)且结构简洁的数据结构模板库, 可使用xmake进行快速构建&测试 | | [Sunrisepeak](https://github.com/Sunrisepeak) | [KHistory](https://github.com/Sunrisepeak/KHistory) (An elegant keyboard/gamepad key detection and visualization tool) | 🔥一个优雅&跨平台的 键盘/🎮手柄按键 检测及历史记录显示工具, 无需安装单可执行文件(约900kb大小)即点即用, 并提供了可视化插件扩展功能, 同时也由于xmake对lua脚本的支持&构建规则, 以简洁的方式在编译期实现了自定义插件的自动注册功能 | | [VeroFess](https://github.com/VeroFess) | [PalWorld-Server-Unoffical-Api](https://github.com/VeroFess/PalWorld-Server-Unoffical-Api) (A PalWorld Server API like minecraft bukkit) | 一个为游戏幻兽帕鲁服务端编写的第三方 API,使用 xmake 进行跨平台编译,和cmake比起来可真是太节约生命了 | | [laugh12321](https://github.com/laugh12321) | [TensorRT-YOLO](https://github.com/laugh12321/TensorRT-YOLO) | 🚀 TensorRT-YOLO:使用TensorRT推理并集成EfficientNMS,支持YOLOv5、YOLOv8、YOLOv9、PP-YOLOE!TensorRT-YOLO利用xmake进行编译,这使得开发者可以更专注于代码编写,而不是构建过程。| | [JXMaster](https://github.com/JX-Master) | [LunaSDK](https://github.com/JX-Master/LunaSDK) | 一个C++的实时渲染框架,使用xmake作为构建系统,支持shader离线编译、资产打包等自定义流程,并支持Windows、macOS等多个系统的应用开发。 | | [ygf](https://github.com/cn-ygf) | [ylis](https://github.com/cn-ygf/ylis) 一个`windows`安装包制作工具,使用`lua`脚本引擎,支持大部分安装场景(注册表、服务、执行命令等),适合中小型软件的安装包制作,比`NSIS`方便。| 感觉`xmake`感觉比`cmake`和`Visual Studio`好用多了,IDE改编译选项不灵活,`cmake`语法学不会 | --- --- url: /zh/about/sponsor.md --- Xmake 项目属于个人开源项目,它的发展需要您的帮助。如果您愿意支持 Xmake 项目的开发,欢迎为其捐赠,支持它的发展。 欢迎联系我们,获取更多赞助详情。 * [联系方式](/zh/about/contact) ## 赞助方式 ### 支付宝 账号:waruqi@gmail.com ### 微信 ### GitHub Sponsor [![GitHub Sponsor](/assets/img/github_sponsor.png)](https://github.com/sponsors/waruqi) ### PayPal [![PayPal Me](/assets/img/paypal.png)](https://paypal.me/tboox/5) ### OpenCollective \[[Become a Sponsor](https://opencollective.com/xmake#sponsor)] ## 赞助等级 ### $5/月 ☕️ - 每月请我喝一杯咖啡。 * 您将在您的个人资料上获得赞助者徽章! ### $10/月 🙏 - 非常感谢您的赞助!您将获得: * 所有前一等级的奖励 * Discord 频道内标记为赞助者 * 您的名字将被添加到我个人网站的支持者名单中 ### $20/月 🍨 - 非常感谢您的赞助!您将获得: * 所有前一等级的奖励 * 优先处理您的 Issues ### $50/月 🐹 - 非常感谢您的赞助!您将获得: * 所有前一等级的奖励 * 帮您审查 xmake.lua 并提供改进建议 ### $200/月 🐴 - 非常感谢您的赞助!您将获得: * 所有前一等级的奖励 * 在我主要开源项目的 README.md 中添加您的 logo 和链接 * 一对一技术咨询服务 ### $500/月 🐬 - 非常感谢您的赞助!您将获得: * 所有前一等级的奖励 * 在我主要开源项目网站添加大 logo 和链接 ### $1000/月 ❤️ - 哇,非常感谢您的慷慨赞助! * 所有前一等级的奖励 * 请联系我,讨论您希望获得的专属回馈! ## 支持者 | 日期 | 支持者 | 渠道 | 金额 | 评价 | |-- | -- | -- | -- | -- | | 2024.04.08 | Maxwell | 微信 | ¥1024 | | | 2024.03.15 | h\*] | 微信 | ¥20 | | | 2024.03.17 | TomCat | 支付宝 | ¥20 | 感谢作者啊,请你喝咖啡 | | 2024.03.13 | H\*s | 微信 | ¥5 | | | 2024.03.08 | \*秀 | 微信 | ¥20 | | | 2024.03.02 | *丘 | 微信 | ¥100 | | | 2024.02.29 | archer正 | 支付宝 | ¥5 | 感谢群主回答我问题,解了急事 | | 2024.02.26 | s*e | 微信 | ¥50 | | | 2024.02.17 | *鸟 | 微信 | ¥3 | | | 2024.02.08 | T*7 | 微信 | ¥10 | | | 2024.01.31 | 振豪 | 支付宝 | ¥50 | 苦 cmake 久已,支持国产 xmake | | 2024.01.11 | \*生 | 微信 | ¥100 | | | 2023.12.29 | *业 | 微信 | ¥10 | | | 2023.12.23 | D*t | 微信 | ¥150 | | | 2023.12.12 | *非 | 微信 | ¥50 | | | 2023.11.23 | jg | 支付宝 | ¥10 | | | 2023.11.22 | doukeran | 支付宝 | ¥10 | 加油!xmake好用,感谢! | | 2023.11.17 | jadedrip | 支付宝 | ¥5 | | | 2023.11.14 | \* | 微信 | ¥50 | | | 2023.11.13 | 玮 | 支付宝 | ¥10 | 支持国产软件! | | 2023.11.08 | 灵均 | 支付宝 | ¥100 | 祝阖家安康,xmake越来越好用! | | 2023.11.05 | 走钢丝 | 支付宝 | ¥168 | 用了下,感觉不错! | | 2023.10.27 | *利 | 微信 | ¥100 | | | 2023.10.18 | F2 | 微信 | ¥20 | | | 2023.10.05 | C*g | 微信 | ¥5 | | | 2023.09.28 | \* | 微信 | ¥50 | | | 2023.09.22 | \* | 微信 | ¥50 | | | 2023.09.20 | V*e | 微信 | ¥20 | | | 2023.09.17 | \*阴 | 微信 | ¥20 | | | 2023.09.07 | \*物 | 微信 | ¥20 | | | 2023.08.23 | 黑白 | 支付宝 | ¥20 | 开源不易,请大佬喝杯奶茶 | | 2023.08.17 | ivanllen | 微信 | ¥200 | 感谢 ruki,给了很多帮助 | | 2023.08.11 | tjan | 支付宝 | ¥58 | | | 2023.08.06 | \*好 | 微信 | ¥20 | 非常感谢作者维护这样一个好用的构建工具 | | 2023.08.06 | *子 | 微信 | ¥20 | 感谢开发和维护 xmake | | 2023.07.30 | *东 | 微信 | ¥100 | | | 2023.07.26 | Deep鑫 | 支付宝 | ¥200 | 感谢答疑,希望 xmake 越来越好 | | 2023.07.22 | 候*7 | 微信 | ¥70 | | | 2023.07.22 | 志超 | 支付宝 | ¥100 | 支持一下 | | 2023.07.17 | *狼 | 微信 | ¥106 | | | 2023.07.07 | Neo | 微信 | ¥10 | 希望 xmake 越做越好 | | 2023.06.21 | 小鸡快跑 | 微信 | ¥10 | 是比 cmake 更好用的构建工具 | | 2023.06.20 | 阿诺 | 支付宝 | ¥200 | 希望 xmake 越来越好 | | 2023.06.17 | 葡* | 微信 | ¥29.9 | | | 2023.06.06 | 蓝颜 | 支付宝 | ¥50 | xmake 很好用,已经推荐给朋友们用 | | 2023.06.04 | i*t | 微信 | ¥18.88 | | | 2023.05.18 | 晚风 | 支付宝 | ¥20 | 支持! | | 2023.05.15 | *侠 | 微信 | ¥19.57 | | | 2023.04.24 | 好吧,注册一下 | 支付宝 | ¥100 | xmake 天下第一 | | 2023.04.21 | 悬阳 | 支付宝 | ¥50 | | | 2023.04.17 | 文强 | 微信 | ¥19.88 | | | 2023.04.13 | *焜 | 微信 | ¥50 | | | 2023.03.19 | *鸟 | 微信 | ¥3 | | | 2023.03.18 | *戴 | 微信 | ¥100 | 感谢 xmake | | 2023.03.10 | 翅膀 | 微信 | ¥200 | 支持一下 xmake | | 2023.02.05 | l*2 | 微信 | ¥300 | | | 2023.02.02 | \* | 微信 | ¥5 | 国产加油 | | 2023.02.01 | *X | 微信 | ¥256 | | | 2023.01.31 | *乐 | 微信 | ¥66 | cmake 反人类设计,这下清爽了,顶! | | 2023.01.18 | r*t | 微信 | ¥100 | | | 2023.01.17 | Maxwell | 微信 | ¥1024 | | | 2023.01.16 | 繁星明 | 微信 | ¥50 | 好用 | | 2023.01.11 | 知而无知 | 微信 | ¥50 | xmake加油! | | 2023.01.11 | 熊猫儿沈浪 | 微信 | ¥100 | | | 2022.12.22 | 我出趟远门 | 支付宝 | ¥100 | | | 2022.12.14 | 1*1 | 微信 | ¥77 | | | 2022.12.02 | l*u | 微信 | ¥16.8 | 加油 | | 2022.11.30 | 高洁 | 支付宝 | ¥50 | 极大减轻了构建c++项目的成本 | | 2022.11.19 | *埃 | 微信 | ¥50 | | | 2022.11.11 | 王王王 | 支付宝 | ¥100 | | | 2022.10.26 | D*w | 微信 | ¥30 | 支持好用,希望越做越好 | | 2022.10.11 | N*e | 微信 | ¥50 | 感谢大佬的工作 | | 2022.09.24 | *谔 | 微信 | ¥100 | 加油 | | 2022.08.31 | 九州 | 支付宝 | ¥100 | 用了一年,感谢作者的辛苦 | | 2022.07.24 | 未来十年 | 支付宝 | ¥50 | xmake 最终会统一 C++ 包管理 | | 2022.07.24 | *大 | 微信 | ¥50 | 赞助 xmake 项目 | | 2022.07.17 | 王逸涵 | 支付宝 | ¥100 | 支持!!! | | 2022.07.17 | Quincy | 微信 | ¥200 | 开源不易,xmake 非常好用 | | 2022.07.09 | *洋 | 微信 | ¥66 | | | 2022.07.07 | 卷云舒 | 支付宝 | ¥100 | 支持国产支持开源!加油兄弟!我是知乎上的 | | 2022.07.07 | 魏文* | 支付宝 | ¥100 | 体验比cmake丝滑多了 | | 2022.06.30 | 小明 | 支付宝 | ¥30 | 加油 | | 2022.06.28 | Bain | 支付宝 | ¥10 | | | 2022.06.22 | 魏文* | 支付宝 | ¥100 | 支持国产,C++开源界因xmake更美好~ | | 2022.06.14 | K*u | 微信 | ¥10 | | | 2022.06.08 | \* | 微信 | ¥81.54 | | | 2022.05.11 | *庭 | 微信 | ¥50 | | | 2022.04.28 | m*l | 微信 | ¥99 | | | 2022.04.21 | 寻找地平线 | 微信 | ¥128 | | | 2022.04.09 | 子慧 | 支付宝 | ¥50 | 非常好用,简洁清晰 | | 2022.04.07 | \* | 微信 | ¥100 | | | 2022.04.05 | \* | 微信 | ¥5 | | | 2022.03.30 | \* | 微信 | ¥5 | | | 2022.03.05 | \* | 微信 | ¥15 | | | 2022.02.04 | *雯 | 支付宝 | ¥10 | 加油 | | 2022.01.25 | 龙伟 | 支付宝 | ¥10 | 生成的目录结构确实比 cmake 强 | | 2021.12.24 | xiaohui | 微信 | ¥50 | | | 2021.12.14 | SirLynix | Github | $10 | | | 2021.12.14 | sytru | 支付宝 | ¥50 | | | 2021.12.14 | 翅膀 | 微信 | ¥100 | | | 2021.11.15 | 朱* | 支付宝 | ¥50 | xmake非常好用,继续加油! | | 2021.10.04 | S*o | 微信 | ¥100 | 加油 | | 2021.09.23 | fhhddgkhg | 支付宝 | ¥20 | | | 2021.09.06 | Stefan Boberg | Paypal | € 25 | | | 2021.09.01 | 姚冬 | 微信 | ¥1024 | 祝 xmake 越做越好 | | 2021.08.22 | Alvin-co | 微信 | ¥100 | | | 2021.08.17 | 九州 | 支付宝 | ¥100 | | | 2021.07.29 | chaney | 支付宝 | ¥1000 | xmake 很好用 | | 2021.06.30 | \*剑 | 微信 | ¥100 | 大佬牛逼,给你点个赞 | | 2021.06.08 | Daniel Roussel | Paypal | 5 EUR | | | 2021.05.31 | 国栋 | 支付宝 | ¥100 | 加油 ^ O ^ ~ 做的好! | | 2021.05.26 | jerry | 支付宝 | ¥100 | 感谢对开源世界的帮助! | | 2021.05.02 | 乐 | 支付宝 | ¥20 | | | 2021.04.26 | 灿辉 | 支付宝 | ¥20 | | | 2021.04.04 | \*烨 | 微信 | ¥50 | | | 2021.03.31 | *晨 | 微信 | ¥10 | | | 2021.03.18 | *博 | 微信 | ¥50 | | | 2021.03.17 | *博 | 微信 | ¥100 | | | 2021.03.14 | Gavin Ray | OpenCollective | $10 | | | 2021.02.07 | 抚* | 支付宝 | ¥66 | xmake牛批! | | 2021.02.05 | *\_ | 微信 | ¥5 | | | 2020.12.05 | 知而无知 | 微信 | ¥50 | | | 2020.12.05 | fghuh | 支付宝 | ¥10 | | | 2020.12.05 | @ | 微信 | ¥5 | | | 2020.11.27 | 少东 | 支付宝 | ¥2.33 | | | 2020.11.25 | 小弧光 | 微信 | ¥10 | | | 2020.11.20 | Russell Haley | Paypal | $25CAD | | | 2020.11.11 | Russell Haley | Paypal | $50CAD | | | 2020.10.23 | Cartesian Technology | Paypal | 5 EUR | | | 2020.10.19 | Cartesian Technology | Paypal | 5 EUR | | | 2020.09.22 | Russell Haley | Paypal | $25CAD | | | 2020.09.13 | Cartesian Technology | Paypal | 5 EUR | | | 2020.09.11 | Cartesian Technology | Paypal | 5 EUR | | | 2020.09.11 | 复刻回忆 | 支付宝 | ¥25 | 加油! | | 2020.09.05 | M*x | 微信 | ¥100 | | | 2020.07.21 | 简单 | 微信 | ¥100 | | | 2020.06.24 | *三 | 微信 | ¥10 | xmake很好用 | | 2020.06.13 | Ronald | 支付宝 | ¥10 | 方便、专业、谢谢 | | 2020.06.13 | c*o | 微信 | ¥100 | | | 2020.06.09 | w*; | 微信 | ¥50 | 我用过最舒心的工具 | | 2020.06.09 | 凌风 | 支付宝 | ¥0.01 | | | 2020.05.25 | 魔之左手 | 微信 | ¥100 | | | 2020.05.20 | Russell Haley | Paypal | $10CAD | | | 2020.05.15 | Russell Haley | Paypal | $10CAD | | | 2020.04.10 | C*g | 微信 | ¥20 | 拒绝白嫖,支持一下 | | 2020.04.07 | *子 | 微信 | ¥10 | 感谢你创造了xmake | | 2019.10.31 | 刘* | 支付宝 | ¥100 | xmake非常好用,期待持续更新,加油! | | 2019.10.05 | 1m188 | 支付宝 | ¥50 | 相比cmake写起来要简单清晰一些 | | 2019.09.15 | ryan | 微信 | ¥100 | | | 2019.06.19 | 匿名 | 微信 | ¥10 | 比cmake好用 | | 2018.11.16 | 孙果 | 微信 | ¥10 | xmake很好用,加油! | | 2018.10.17 | 红红 | 微信 | ¥100 | | | 2018.10.15 | xtvjxk | 支付宝 | ¥10 | 希望越来越好用 | | 2018.08.07 | fasiondog | gitee | ¥50 | 感谢您的开源项目! | | 2018.06.27 | Kellygod | 支付宝 | ¥30 | 拥抱xmake | | 2018.05.17 | 匿名 | 微信 | ¥10 | 加油xmake | | 2018.04.29 | 清春 | 支付宝 | ¥180 | | | 2018.02.18 | 氧烷 | 支付宝 | ¥16.66 | xmake,赞👍 | | 2017.11.19 | sytru | 支付宝 | ¥10 | 感谢作者,让写c/c++有了写脚本的感觉。非常愉悦的使用体验,希望不要消失。 | | 2017.11.16 | 琪峻 | 支付宝 | ¥15 | 感谢好用的xmake | | 2017.10.26 | Welthy | 支付宝 | ¥5 | xmake很好用啊 | | 2016.11.10 | [lc-soft](https://github.com/lc-soft) | oschina | ¥10 | | --- --- url: /zh/examples/bindings/nodejs-module.md --- 参考[例子](https://github.com/tonyfettes/coc-rime/blob/master/xmake.lua)。 ```lua add_rules("mode.debug", "mode.release") add_requires("node-addon-api") target("rime") set_languages("cxx17") add_rules("nodejs.module") add_packages("node-addon-api") add_files("*.cc") ``` --- --- url: /zh/examples/bindings/lua-module.md --- 参考 [luarocks-build-xmake](https://github.com/xmake-io/luarocks-build-xmake) 如果你的 lua 模块含有 C 代码,你可以使用 [LuaNativeObjects](https://github.com/Neopallium/LuaNativeObjects) 去从 lua 代码生成 C 代码。 参考[例子](https://github.com/Freed-Wu/rime.nvim/blob/main/xmake.lua)。 ```lua add_rules("mode.debug", "mode.release") target("rime") add_rules("lua.module", "lua.native-objects") add_files("*.nobj.lua") add_cflags("-Wno-int-conversion") ``` --- --- url: /zh/examples/bindings/swig.md --- 2.5.8 版本支持构建 Swig 模块,我们提供了 `swig.c` 和 `swig.cpp` 规则,分别对应支持生成 c/c++ 模块接口代码,配合 xmake 的包管理系统实现完全自动化的模块和依赖包整合。 相关 issues: [#1622](https://github.com/xmake-io/xmake/issues/1622) ## Lua/C 模块 {#lua-c-module} ```lua add_rules("mode.release", "mode.debug") add_requires("lua") target("example") add_rules("swig.c", {moduletype = "lua"}) add_files("src/example.i", {swigflags = "-no-old-metatable-bindings"}) add_files("src/example.c") add_packages("lua") ``` ## Python/C 模块 {#python-c-module} ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.c", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.c") add_packages("python") ``` ## Python/C++ 模块 {#python-cpp-module} ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.cpp", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.cpp") add_packages("python") ``` ## Java/C 模块 {#java-c-module} [完整例子](https://github.com/xmake-io/xmake/blob/dev/tests/projects/swig/java_c) ```lua -- make sure you config to an enviroment with jni.h -- for example: xmake f -c -p android target("example") set_kind('shared') -- set moduletype to java add_rules("swig.c", {moduletype = "java"}) -- test jar build -- add_rules("swig.c", {moduletype = "java" , buildjar = true}) -- use swigflags to provider package name and output path of java files add_files("src/example.i", {swigflags = { "-package", "com.example", "-outdir", "build/java/com/example/" }}) add_files("src/example.c") add_includedirs("src") before_build(function() -- ensure output path exists before running swig os.mkdir("build/java/com/example/") end) ``` 我们也可以配置 ```lua add_rules("swig.c", {moduletype = "java", buildjar = true}) ``` 去同时构建 jar 包,方便直接使用。 --- --- url: /zh/examples/cpp/asn1.md --- ASN.1 程序,需要借助 [ASN.1 Compiler](https://github.com/vlm/asn1c) 去生成相关的 .c 文件参与项目编译。 而 Xmake 内置提供了 `add_rules("asn1c")` 规则去处理 `.c` 文件生成,`add_requires("asn1c")` 自动拉取集成 ASN.1 编译器工具。 下面是一个基础的配置例子: ```lua add_rules("mode.debug", "mode.release") add_requires("asn1c") target("test") set_kind("binary") add_files("src/*.c") add_files("src/*.asn1") add_rules("asn1c") add_packages("asn1c") ``` 具体见 [完整例子工程](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/asn1c)。 --- --- url: /zh/examples/cpp/basic.md --- 下面我们简单介绍一些常用的工程例子,更多更全的examples工程可以到[project examples](https://github.com/xmake-io/xmake/tree/master/tests/projects)中查看。 我们也可以通过:`xmake create`命令创建各种常用的空工程来快速开始,具体对于这个命令的介绍以及支持的工程模板,可以敲下面的命令查看: ```sh xmake create --help ``` ## 可执行程序 {#executable} ```lua target("test") set_kind("binary") add_files("src/*cpp") ``` 完整例子请执行下面的命令来创建: ```sh xmake create test ``` 如果我们想创建 C 语言程序,只需要添加 `-l c` 命令行参数,例如: ```sh xmake create -l c test ``` ## 静态库程序 {#static-library} ```lua target("foo") set_kind("static") add_files("src/foo/*.cpp") target("test") set_kind("binary") add_files("src/*.cpp") add_deps("foo") ``` 通过`add_deps`将一个静态库自动链接到test可执行程序。 完整例子请执行下面的命令来创建: ```sh xmake create -t static test ``` 如果我们想创建 C 语言程序,只需要添加 `-l c` 命令行参数,例如: ```sh xmake create -l c -t static test ``` ## 动态库程序 {#shared-library} ```lua target("foo") set_kind("shared") add_files("src/foo/*.cpp") target("test") set_kind("binary") add_files("src/*.cpp") add_deps("foo") ``` 通过`add_deps`将一个动态库自动链接到test可执行程序。 完整例子请执行下面的命令来创建: ```sh xmake create -t shared test ``` 如果我们想创建 C 语言程序,只需要添加 `-l c` 命令行参数,例如: ```sh xmake create -l c -t shared test ``` --- --- url: /zh/examples/cpp/cosmocc.md --- ```lua add_rules("mode.debug", "mode.release") add_requires("cosmocc") target("test") set_kind("binary") add_files("src/*.c") set_toolchains("@cosmocc") ``` --- --- url: /zh/examples/cpp/cppfront.md --- ```lua add_rules("mode.debug", "mode.release") add_requires("cppfront") target("test") add_rules("cppfront") set_kind("binary") add_files("src/*.cpp2") add_packages("cppfront") ``` --- --- url: /zh/examples/cpp/linux-bpf.md --- 从 2.5.3 之后开始支持 bpf 程序构建,同时支持 linux 以及 android 平台,能够自动拉取 llvm 和 android ndk 工具链。 更多详情见:[#1274](https://github.com/xmake-io/xmake/issues/1274) ```lua add_rules("mode.release", "mode.debug") add_rules("platform.linux.bpf") add_requires("linux-tools", {configs = {bpftool = true}}) add_requires("libbpf") if is_plat("android") then add_requires("ndk >=22.x") set_toolchains("@ndk", {sdkver = "23"}) else add_requires("llvm >=10.x") set_toolchains("@llvm") add_requires("linux-headers") end target("minimal") set_kind("binary") add_files("src/*.c") add_packages("linux-tools", "linux-headers", "libbpf") set_license("GPL-2.0") ``` --- --- url: /zh/examples/cpp/linux-driver-module.md --- v2.6.2 版本,xmake 完整支持了 Linux 内核驱动模块的构建,这也许首个也是唯一一个支持编译 Linux 内核驱动的第三方构建工具了。 ## Hello world 模块 {#hellow-world} 完整例子:[Linux Kernel Driver Modules](https://github.com/xmake-io/xmake/tree/master/tests/projects/linux/driver/hello) 它的配置非常简单,只需要配置上支持模块的 linux-headers 包,然后应用 `platform.linux.module` 构建规则就行了。 ```lua add_requires("linux-headers", {configs = {driver_modules = true}}) target("hello") add_rules("platform.linux.module") add_files("src/*.c") add_packages("linux-headers") set_license("GPL-2.0") ``` 然后直接执行 xmake 命令,一键编译,生成内核驱动模块 hello.ko。 ```sh $ xmake [ 20%]: cache compiling.release src/add.c [ 20%]: cache compiling.release src/hello.c [ 60%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! ``` 我们也可以看完整构建命令参数。 ```sh $ xmake -v [ 20%]: cache compiling.release src/add.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"add\" -o build/.objs/hello/linux/x86_64/release/src/add.c.o src/add.c [ 20%]: cache compiling.release src/hello.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"hello\" -o build/.objs/hello/linux/x86_64/release/src/hello.c.o src/hello.c [ 60%]: linking.release build/linux/x86_64/release/hello.ko /usr/bin/ld -m elf_x86_64 -r -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/src/add.c.o build/.objs/hello/linux/x86_64/release/src/hello.c.o /usr/src/linux-headers-5.11.0-41-generic/scripts/mod/modpost -m -a -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/Module.symvers -e -N -T - WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.c /usr/bin/ld -m elf_x86_64 -r --build-id=sha1 -T /usr/src/linux-headers-5.11.0-41-generic/scripts/module.lds -o build/linux/x86_64/release/hello.ko build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o ``` 通过 `add_requires("linux-headers", {configs = {driver_modules = true}})` 配置包,xmake 会自动优先从系统中查找对应的 linux-headers 包。 如果没找到,xmake 也会自动下载它,然后自动配置构建带有 driver modules 的内核源码后,使用它继续构建内核模块。 ## 自定义 linux-headers 路径 {#custom-linux-headers-path} 自从 v2.6.2 版本发布,有很多用户反馈,大多数情况下,linux 内核驱动构建都是基于定制版的 linux kernel,因此需要能够自定义配置 linux-headers 路径,而不是走远程依赖包模式。 其实,我们通过自己重写 linux-headers 包,也是可以做到这一点的。 ```lua package("linux-headers") on_fetch(function (package, opt) return {includedirs = "/usr/src/linux-headers-5.0/include"} end) package_end() add_requires("linux-headers") target("test") add_rules("platform.linux.module") add_files("src/*.c") add_packages("linux-headers") ``` 不过这样,也许还有点繁琐,因此在 v2.6.3 版本,我们支持更加方便的设置 linux-headers 路径。 ```lua target("hello") add_rules("platform.linux.module") add_files("src/*.c") set_values("linux.driver.linux-headers", "/usr/src/linux-headers-5.11.0-41-generic") ``` 我们也可以通过定义 option 选项,将 linux-headers 路径作为 `xmake f --linux-headers=/usr/src/linux-headers` 的方式传入。 ```lua option("linux-headers", {showmenu = true, description = "Set linux-headers path."}) target("hello") add_rules("platform.linux.module") add_files("src/*.c") set_values("linux.driver.linux-headers", "$(linux-headers)") ``` 更多详情见:[#1923](https://github.com/xmake-io/xmake/issues/1923) ## 交叉编译 {#cross-compilation} 我们也支持内核驱动模块的交叉编译,比如在 Linux x86\_64 上使用交叉编译工具链来构建 Linux Arm/Arm64 的驱动模块。 我们只需要准备好自己的交叉编译工具链,通过 `--sdk=` 指定它的根目录,然后配置切换到 `-p cross` 平台, 最后指定需要构建的架构 arm/arm64 即可。 这里用到的交叉工具链,可以从这里下载: [Download toolchains](https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/) 更多,交叉编译配置文档,见:[配置交叉编译](/zh/guide/basic-commands/cross-compilation)。 ::: tip 注意 目前仅仅支持 arm/arm64 交叉编译架构,后续会支持更多的平台架构。 ::: ### 构建 Arm 驱动模块 {#build-arm-module} ```sh $ xmake f -p cross -a arm --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf -c $ xmake -v checking for arm-linux-gnueabihf-g++ ... /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ checking for the linker (ld) ... arm-linux-gnueabihf-g++ checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-g++ ... ok checking for flags (-fPIC) ... ok checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc ... ok checking for flags (-fPIC) ... ok checking for flags (-O2) ... ok checking for ccache ... /usr/bin/ccache [ 20%]: cache compiling.release src/add.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -c -O2 -std=gnu89 -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -D__LINUX_ARM_ARCH__=6 -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/kconfig.h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -mbig-endian -mabi=aapcs-linux -mfpu=vfp -marm -march=armv6k -mtune=arm1136j-s -msoft-float -Uarm -DKBUILD_BASENAME=\"add\" -o build/.objs/hello/cross/arm/release/src/add.c.o src/add.c [ 20%]: cache compiling.release src/hello.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -c -O2 -std=gnu89 -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -D__LINUX_ARM_ARCH__=6 -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/kconfig.h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -mbig-endian -mabi=aapcs-linux -mfpu=vfp -marm -march=armv6k -mtune=arm1136j-s -msoft-float -Uarm -DKBUILD_BASENAME=\"hello\" -o build/.objs/hello/cross/arm/release/src/hello.c.o src/hello.c checking for flags (-MMD -MF) ... ok checking for flags (-fdiagnostics-color=always) ... ok [ 60%]: linking.release build/cross/arm/release/hello.ko /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-ld -EB -r -o build/.objs/hello/cross/arm/release/build/cross/arm/release/hello.ko.o build/.objs/hello/cross/arm/release/src/add.c.o build/.objs/hello/cross/arm/release/src/hello.c.o /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/scripts/mod/modpost -m -a -o build/.objs/hello/cross/arm/release/build/cross/arm/release/Module.symvers -e -N -T - WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc -c -O2 -std=gnu89 -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/arch/arm/include/generated/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -D__LINUX_ARM_ARCH__=6 -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/../lib/gcc/arm-linux-gnueabihf/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/kconfig.h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -mbig-endian -mabi=aapcs-linux -mfpu=vfp -marm -march=armv6k -mtune=arm1136j-s -msoft-float -Uarm -o build/.objs/hello/cross/arm/release/build/cross/arm/release/hello.ko.mod.o build/.objs/hello/cross/arm/release/build/cross/arm/release/hello.ko.mod.c /mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf/bin/arm-linux-gnueabihf-ld -EB --be8 -r --build-id=sha1 -T /home/ruki/.xmake/packages/l/linux-headers/5.10.46/7695a30b7add4d3aa4685cbac6815805/scripts/module.lds -o build/cross/arm/release/hello.ko build/.objs/hello/cross/arm/release/build/cross/arm/release/hello.ko.o build/.objs/hello/cross/arm/release/build/cross/arm/release/hello.ko.mod.o [100%]: build ok! ``` ### 构建 Arm64 驱动模块 {#build-arm64-module} ```sh $ xmake f -p cross -a arm64 --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu -c checking for aarch64-linux-gnu-g++ ... /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ checking for the linker (ld) ... aarch64-linux-gnu-g++ checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ ... ok checking for flags (-fPIC) ... ok checking for /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc ... ok checking for flags (-fPIC) ... ok checking for flags (-O2) ... ok checking for ccache ... /usr/bin/ccache [ 20%]: cache compiling.release src/add.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -c -O2 -std=gnu89 -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/kconfig.h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"add\" -o build/.objs/hello/cross/arm64/release/src/add.c.o src/add.c [ 20%]: cache compiling.release src/hello.c /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -c -O2 -std=gnu89 -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/kconfig.h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"hello\" -o build/.objs/hello/cross/arm64/release/src/hello.c.o src/hello.c checking for flags (-MMD -MF) ... ok checking for flags (-fdiagnostics-color=always) ... ok [ 60%]: linking.release build/cross/arm64/release/hello.ko /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ld -EL -maarch64elf -r -o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/hello.ko.o build/.objs/hello/cross/arm64/release/src/add.c.o build/.objs/hello/cross/arm64/release/src/hello.c.o /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/scripts/mod/modpost -m -a -o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/Module.symvers -e -N -T - WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc -c -O2 -std=gnu89 -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/arch/arm64/include/generated/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/uapi -I/home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -isystem /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/7.5.0/include -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/kconfig.h -include /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/include/linux/compiler_types.h -nostdinc -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/hello.ko.mod.o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/hello.ko.mod.c /mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu/bin/aarch64-linux-gnu-ld -EL -maarch64elf -r --build-id=sha1 -T /home/ruki/.xmake/packages/l/linux-headers/5.10.46/8f80101835834bc2866f3a827836b5de/scripts/module.lds -o build/cross/arm64/release/hello.ko build/.objs/hello/cross/arm64/release/build/cross/arm64/release/hello.ko.o build/.objs/hello/cross/arm64/release/build/cross/arm64/release/hello.ko.mod.o [100%]: build ok! ``` --- --- url: /zh/examples/cpp/merge-static-libraries.md --- ## 自动合并 target 库 {#auto-merge-target-libs} 2.5.8 之后,我们可以通过设置 `build.merge_archive` 策略,启用自动合并依赖的所有静态库,例如: ```lua add_rules("mode.debug", "mode.release") target("add") set_kind("static") add_files("src/add.c") add_files("src/subdir/add.c") target("sub") set_kind("static") add_files("src/sub.c") add_files("src/subdir/sub.c") target("mul") set_kind("static") add_deps("add", "sub") add_files("src/mul.c") set_policy("build.merge_archive", true) ``` mul 静态库自动合并了 add 和 sub 静态库,生成一个包含 add/sub 代码的完整 libmul.a 库。 这个合并相对比较稳定完善,支持 ar 和 msvc/lib.exe,也支持交叉编译工具链生成的静态库合并,也支持带有重名 obj 文件的静态库。 ## 合并指定的静态库文件 {#merge-specific-static-libs} 如果自动合并不满足需求,我们也可以主动调用 `utils.archive.merge_archive` 模块在 `after_link` 阶段合并指定的静态库列表。 ```lua target("test") after_link(function (target) import("utils.archive.merge_staticlib") merge_staticlib(target, "libout.a", {"libfoo.a", "libbar.a"}) end) ``` ## 使用 add\_files 合并静态库 {#using-add\_files} 其实,我们之前的版本已经支持通过 `add_files("*.a")` 来合并静态库。 ```lua target("test") set_kind("binary") add_files("*.a") add_files("*.c") ``` 相关 issues: [#1638](https://github.com/xmake-io/xmake/issues/1638) --- --- url: /zh/examples/cpp/mfc.md --- ## MFC 静态库 {#mfc-static} ```lua target("test") add_rules("win.sdk.mfc.static") add_files("src/*.c") ``` ## MFC 动态库 {#mfc-shared} ```lua target("test") add_rules("win.sdk.mfc.shared") add_files("src/*.c") ``` ## MFC 应用程序(静态链接){#mfc-app-static} ```lua target("test") add_rules("win.sdk.mfc.static_app") add_files("src/*.c") ``` ## MFC 应用程序(动态链接){#mfc-app-shared} ```lua target("test") add_rules("win.sdk.mfc.shared_app") add_files("src/*.c") ``` --- --- url: /zh/examples/cpp/openmp.md --- v2.6.1 以后,改进了 openmp 的配置,更加简化和统一,我们不再需要额外配置 rules,仅仅通过一个通用的 openmp 包就可以实现相同的效果。 ```lua add_requires("openmp") target("loop") set_kind("binary") add_files("src/*.cpp") add_packages("openmp") ``` v2.5.9 之前的版本 ```lua add_requires("libomp", {optional = true}) target("loop") set_kind("binary") add_files("src/*.cpp") add_rules("c++.openmp") add_packages("libomp") ``` 如果是c代码,需要启用 `add_rules("c.openmp")`,如果是 c/c++ 混合编译,那么这两个规则都要设置。 --- --- url: /zh/examples/cpp/qt.md --- 创建一个空工程: ```sh $ xmake create -t qt.console test $ xmake create -t qt.static test $ xmake create -t qt.shared test $ xmake create -t qt.quickapp test $ xmake create -t qt.widgetapp test ``` 更多工程模板见:`xmake create --help` 默认会自动探测Qt环境,当然也可以指定Qt SDK环境目录: ```sh $ xmake f --qt=~/Qt/Qt5.9.1 ``` 如果想要使用 windows 下 MingW 的 Qt 环境,可以切到mingw的平台配置,并且指定下mingw编译环境的sdk路径即可,例如: ```sh $ xmake f -p mingw --sdk=C:\Qt\Qt5.10.1\Tools\mingw530_32 ``` 上述指定的 MingW SDK 用的是Qt下Tools目录自带的环境,当然如果有其他第三方 MingW 编译环境,也可以手动指定, 具体可以参考:[MingW 编译配置](/zh/guide/basic-commands/build-configuration#mingw)。 更多详情可以参考:[#160](https://github.com/xmake-io/xmake/issues/160) 另外,当前xmake也支持Qt/Wasm,详情见:[Wasm 配置](/zh/guide/basic-commands/build-configuration#wasm-webassembly) ```sh $ xmake f -p wasm ``` ## 静态库程序 {#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} ```lua target("qt_console") add_rules("qt.console") add_files("src/*.cpp") ``` ## Quick 应用程序 {#quickapp} ```lua target("qt_quickapp") add_rules("qt.quickapp") add_files("src/*.cpp") add_files("src/qml.qrc") ``` ::: tip 注意 如果使用的自己编译的static版本QT SDK,那么需要切换到`add_rules("qt.quickapp_static")`静态规则才行,因为链接的库是不同的,需要做静态链接。 ::: 接下来,我们尝试编译下,通常,如果是使用Qt的安装包默认安装,也没有修改安装路径,那么大部分情况下都是可以自动检测到QT SDK的根路径,例如: ```sh $ xmake checking for the architecture ... x86_64 checking for the Xcode directory ... /Applications/Xcode.app checking for the SDK version of Xcode ... 10.15 checking for the Qt SDK directory ... /Users/ruki/Qt5.13.2/5.13.2/clang_64 checking for the Qt SDK version ... 5.13.2 [ 0%]: cache compiling.release src/main.cpp [ 49%]: compiling.qt.qrc src/qml.qrc [100%]: linking.release test build ok! ``` 然后我们继续运行下它: ```sh $ xmake run ``` 效果如下: ![](/assets/img/guide/qt_quickapp.png) ## Quick Plugin 程序 {#quick-plugin} 完整例子见:[quickplugin example](https://github.com/xmake-io/xmake/tree/master/tests/projects/qt/quickplugin) ```lua add_rules("mode.debug", "mode.release") target("demo") add_rules("qt.qmlplugin") add_headerfiles("src/*.h") add_files("src/*.cpp") set_values("qt.qmlplugin.import_name", "My.Plugin") ``` ## Widgets 应用程序 {#widgetapp} ```lua target("qt_widgetapp") add_rules("qt.widgetapp") add_files("src/*.cpp") add_files("src/mainwindow.ui") add_files("src/mainwindow.h") -- 添加带有 Q_OBJECT 的meta头文件 ``` ::: tip 注意 如果使用的自己编译的static版本QT SDK,那么需要切换到`add_rules("qt.widgetapp_static")`静态规则才行,因为链接的库是不同的,需要做静态链接。 ::: 运行效果如下: ![](/assets/img/guide/qt_widgetapp.png) ## Android 应用程序 {#android-app} 2.2.6 之后版本,可以直接切到android平台编译Quick/Widgets应用程序,生成apk包,并且可通过`xmake install`命令安装到设备。 ```sh $ 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%]: cache compiling.release src/main.cpp [100%]: linking.release libappdemo.so [100%]: generating.qt.app appdemo.apk ``` 然后安装到设备: ```sh $ xmake install installing appdemo ... installing build/android/release/appdemo.apk .. Success install ok!👌 ``` ## 目前支持的 Qt SDK {#supported-qtsdks} ### 来自 Qt 官方提供的 SDK 安装包 在 macos/windows 上通常能自动探测到,但是也可以手动指定 Qt SDK 路径。 ```sh $ xmake f --qt=[qt sdk path] ``` ### 来自 Ubuntu Apt 安装包 使用 apt 安装完 Qt SDK,xmake 也能够自动检测到。 ```sh $ sudo apt install -y qtcreator qtbase5-dev $ xmake ``` ### 来自 msys2/pacman 的 Qt Mingw 安装包 xmake 也支持从 pacman 安装的 Qt Mingw SDK ```sh $ pacman -S mingw-w64-x86_64-qt5 mingw-w64-x86_64-qt-creator $ xmake ``` ### 来自 aqtinstall 脚本的 Qt SDK 包 [aqtinstall](https://github.com/miurahr/aqtinstall) 安装的 Qt SDK 是完全基于官方 SDK 结构的,所以 xmake 也完全支持。 但是,通常需要自己指定 SDK 路径。 ```sh $ xmake f --qt=[Qt SDK] ``` ### 跨平台 Qt 交叉编译 对于跨平台 Qt 开发,xmake 支持为主机工具和目标平台使用单独的 SDK。这在为不同于开发机器的平台构建 Qt 应用程序时特别有用。 `--qt_host` 选项允许您指定与构建机器兼容的 Qt 工具的位置,而 `--qt` 指向目标平台的 SDK: ```sh $ xmake f --qt=[target Qt sdk] --qt_host=[host Qt sdk] ``` ::: tip 注意 * 确保主机和目标 Qt 版本匹配,否则可能会导致构建问题。 * 本机部署工具(如 `windeployqt` 和 `macdeployqt`)必须在各自的平台上运行,因此跨平台任务(如 `xmake install`)可能会失败。 ::: ### 来自 xmake-repo 仓库的 Qt 包 xmake 现在官方提供了 Qt5 和 Qt6 SDK 的各种模块包,可以自动集成使用,无需任何手动安装。 只需要配置集成包就行了,xmake 会自动处理 Qt 的安装集成,并且自动编译项目。 #### Qt5 示例 ```lua add_rules("mode.debug", "mode.release") add_requires("qt5widgets") target("test") add_rules("qt.widgetapp") add_packages("qt5widgets") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/mainwindow.ui") -- add files with Q_OBJECT meta (only for qt.moc) add_files("src/mainwindow.h") ``` #### Qt6 示例 ```lua add_rules("mode.debug", "mode.release") add_requires("qt6widgets") target("test") add_rules("qt.widgetapp") add_packages("qt6widgets") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/mainwindow.ui") -- add files with Q_OBJECT meta (only for qt.moc) add_files("src/mainwindow.h") ``` 除了 `qt6widgets` 包,仓库还提供了 `qt6gui`, `qt6network` 等 Qt6 包,可以使用。 配置完,只需要执行: ```sh $ xmake ``` ::: tip 注意 Qt6 的包现已支持,可同时使用 Qt5 和 Qt6 包。 ::: ### 来自 vcpkg/conan 的 Qt 包 暂时还没时间支持,请尽量使用上面的方式集成 Qt SDK。 --- --- url: /zh/examples/cpp/protobuf.md --- ## 使用 C 库 {#use-c-library} ```lua add_requires("protobuf-c") target("console_c") set_kind("binary") add_packages("protobuf-c") add_rules("protobuf.c") add_files("src/*.c") add_files("src/*.proto") ``` 我们还可以设置 `proto_public = true` 来导出 proto 的头文件搜索目录,开放给其他父 target 继承使用。 ```lua add_packages("protobuf-c", {public = true}) add_files("src/**.proto", {proto_public = true}) ``` ::: tip 注意 由于 protobuf 生成的头文件引用了 protobuf-c 包的头文件,因此,我们也需要将包的头文件标记为 `{public = true}` 对外导出它。 ::: ## 使用 C++ 库 {#use-cpp-library} ```lua add_requires("protobuf-cpp") target("console_c++") set_kind("binary") set_languages("c++11") add_packages("protobuf-cpp") add_rules("protobuf.cpp") add_files("src/*.cpp") add_files("src/*.proto") ``` 我们还可以设置 `proto_public = true` 来导出 proto 的头文件搜索目录,开放给其他父 target 继承使用。 ```lua add_packages("protobuf-cpp", {public = true}) add_files("src/**.proto", {proto_public = true}) ``` ::: tip 注意 由于 protobuf 生成的头文件引用了 protobuf-cpp 包的头文件,因此,我们也需要将包的头文件标记为 `{public = true}` 对外导出它。 ::: --- --- url: /zh/examples/cpp/wasm.md --- 所有 c/c++ 程序,我们都可以编译成 Wasm,无需任何 xmake.lua 配置改动,只需要切换到 wasm 编译平台进行编译。 ```sh $ xmake f -p wasm $ xmake ``` 详细的 Wasm 编译配置见:[Wasm 配置](/zh/guide/basic-commands/build-configuration.html#wasm-webassembly)。 另外,在编译带有 `--preload-file assets/xxx.md` 设置的文件时候,我们也可以通过配置,简化对它的设置。 ```lua target("test5") set_kind("binary") add_files("src/*.cpp") add_values("wasm.preloadfiles", "src/xxx.md") add_values("wasm.preloadfiles", "src/xxx2.md") ``` --- --- url: /zh/examples/cpp/winsdk.md --- ```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) --- --- url: /zh/examples/embed/keil-mdk.md --- 相关例子工程:[Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/embed/mdk/hello) xmake 会自动探测 Keil/MDK 安装的编译器,相关 issues [#1753](https://github.com/xmake-io/xmake/issues/1753)。 使用 armcc 编译 ```sh $ xmake f -p cross -a cortex-m3 --toolchain=armcc -c $ xmake ``` 使用 armclang 编译 ```sh $ xmake f -p cross -a cortex-m3 --toolchain=armclang -c $ xmake ``` ## 可执行程序 {#executable} ```lua target("hello") add_deps("foo") add_rules("mdk.binary") add_files("src/*.c", "src/*.s") add_includedirs("src/lib/cmsis") set_runtimes("microlib") ``` 需要注意的是,目前一些 mdk 程序都使用了 microlib 库运行时,它需要编译器加上 `__MICROLIB` 宏定义,链接器加上 `--library_type=microlib` 等各种配置。 我们可以通过 `set_runtimes("microlib")` 直接设置到 microlib 运行时库,可以自动设置上所有相关选项。 ## 静态库程序 {#static-library} ```lua add_rules("mode.debug", "mode.release") target("foo") add_rules("mdk.static") add_files("src/foo/*.c") set_runtimes("microlib") ``` --- --- url: /zh/examples/cpp/wdk.md --- 默认会自动探测 WDK 所在环境,当然也可以指定 WDK sdk 环境目录: ```sh $ xmake f --wdk="G:\Program Files\Windows Kits\10" -c $ xmake ``` 更多详情可以参考:[#159](https://github.com/xmake-io/xmake/issues/159) 相关完整工程 example 见:[WDK examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/windows/driver) ## umdf 驱动程序{#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 驱动程序 {#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 驱动程序 {#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"}}) ``` ## 生成驱动包 {#package} 可以通过以下命令生成.cab驱动包: ```sh $ 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 ``` ## 驱动签名 {#driver-sign} 默认编译禁用签名,可以通过 `set_values("wdk.sign.mode", ...)` 设置签名模式来启用签名。 ### 测试签名 {#test-sign} 测试签名一般本机调试时候用,可以使用 xmake 自带的 test 证书来进行签名,例如: ```lua target("msdsm") add_rules("wdk.driver", "wdk.env.wdm") set_values("wdk.sign.mode", "test") set_values("wdk.sign.digest_algorithm", "sha256") ``` 不过这种情况下,需要用户手动在管理员模式下,执行一遍:`$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") set_values("wdk.sign.digest_algorithm", "sha256") ``` 从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)") set_values("wdk.sign.digest_algorithm", "sha256") ``` ### 正式签名 {#sign} 通过指定对应的正式签名证书文件进行签名: ```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")) set_values("wdk.sign.digest_algorithm", "sha256") ``` ## 生成低版本驱动 {#low-version-driver} 如果想在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版本: ```sh $ xmake f --wdk_winver=[win10_rs3|win8|win7|win7_sp1] $ xmake ``` --- --- url: /zh/examples/embed/keil-c51.md --- ## 可执行程序 {#executable} ```lua target("hello") add_rules("c51.binary") set_toolchains("c51") add_files("src/main.c") ``` --- --- url: /zh/examples/other-languages/fortran.md --- v2.3.6之后版本开始支持gfortran编译器来编译fortran项目,我们可以通过下面的命令,快速创建一个基于fortran的空工程: v2.3.8之后,xmake 还支持 Intel Fortran Compiler,只需要切换下工具链即可:`xmake f --toolchain=ifort` ```sh $ xmake create -l fortran -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.f90") ``` 更多代码例子可以到这里查看:[Fortran Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/fortran) --- --- url: /zh/examples/other-languages/dlang.md --- 创建空工程: ```sh $ xmake create -l dlang -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.d") ``` v2.3.6 版本开始,Xmake 增加了对dub包管理的支持,可以快速集成dlang的第三方依赖包: ```lua add_rules("mode.debug", "mode.release") add_requires("dub::log 0.4.3", {alias = "log"}) add_requires("dub::dateparser", {alias = "dateparser"}) add_requires("dub::emsi_containers", {alias = "emsi_containers"}) add_requires("dub::stdx-allocator", {alias = "stdx-allocator"}) add_requires("dub::mir-core", {alias = "mir-core"}) target("test") set_kind("binary") add_files("src/*.d") add_packages("log", "dateparser", "emsi_containers", "stdx-allocator", "mir-core") ``` 不过还有一些不完善的地方,比如目前必须手动配置所有级联依赖包,会稍微繁琐些,后续有待改进。 更多例子见:[Dlang Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/dlang) --- --- url: /zh/examples/other-languages/cuda.md --- 创建一个空工程: ```sh $ xmake create -P test -l cuda $ cd test $ xmake ``` ```lua 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") ``` ::: tip 注意 从v2.2.7版本开始,默认构建会启用device-link。(参见 [Separate Compilation and Linking of CUDA C++ Device Code](https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/)) 如果要显式禁用device-link,可以通过 `set_policy("build.cuda.devlink", false)` 来设置。 ::: ::: tip 注意 cuda 源文件中的 device 函数需要被 device-link 且只 device-link 一次。在 `shared` 或 `binary` 的 target 上 xmake 会自动进行 device-link ,这时它们依赖的 `static` target 也会同时被 device-link ,因此默认情况下 `static` target 不会被 device-link。然而,如果最终的 `shared` 或 `binary` 的 target 不包含任何 cuda 源文件,则不会发生 device-link 阶段,导致出现 undefined reference 错误。这种情况下,需要手动为 `static` target 指定 `add_values("cuda.build.devlink", true)`. ::: 默认会自动探测 Cuda 环境,当然也可以指定 Cuda SDK 环境目录,或者指定 Cuda 版本(此时将在默认安装目录进行查找): ```sh $ xmake f --cuda=/usr/local/cuda-9.1/ $ xmake f --cuda=9.1 $ xmake ``` 更多详情可以参考:[#158](https://github.com/xmake-io/xmake/issues/158) --- --- url: /zh/examples/other-languages/golang.md --- Xmake 也支持 GO 程序的构建,也提供了空工程的创建命令支持: ```sh $ xmake create -l go -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.go") ``` v2.3.6版本,xmake对其的构建支持做了一些改进,对go的交叉编译也进行了支持,例如我们可以在macOS和linux上编译windows程序: ```sh $ xmake f -p windows -a x86 ``` 另外,新版本对go的第三方依赖包管理也进行了初步支持: ```lua add_rules("mode.debug", "mode.release") add_requires("go::github.com/sirupsen/logrus", {alias = "logrus"}) add_requires("go::golang.org/x/sys/internal/unsafeheader", {alias = "unsafeheader"}) if is_plat("windows") then add_requires("go::golang.org/x/sys/windows", {alias = "syshost"}) else add_requires("go::golang.org/x/sys/unix", {alias = "syshost"}) end target("test") set_kind("binary") add_files("src/*.go") add_packages("logrus", "syshost", "unsafeheader") ``` 不过还有一些不完善的地方,比如目前必须手动配置所有级联依赖包,会稍微繁琐些,后续有待改进。 更多例子见:[Go Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/go) --- --- url: /zh/examples/other-languages/lex-yacc.md --- ```lua target("calc")      set_kind("binary")      add_rules("lex", "yacc")      add_files("src/*.l", "src/*.y") ``` --- --- url: /zh/examples/other-languages/nim.md --- v2.5.9 之后,我们新增了对 Nimlang 项目的支持,相关 issues 见:[#1756](https://github.com/xmake-io/xmake/issues/1756) ## 创建空工程 {#create-project} 我们可以使用 `xmake create` 命令创建空工程。 ```sh xmake create -l nim -t console test xmake create -l nim -t static test xmake create -l nim -t shared test ``` ## 控制台程序 {#console} ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/main.nim") ``` ```sh $ xmake -v [ 33%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache -o:b uild/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` ## 静态库程序 {#static-library} ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```sh $ xmake -v [ 33%]: linking.release libfoo.a /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :staticlib --noMain --passC:-DNimMain=NimMain_B6D5BD02 --passC:-DNimMainInner=NimMainInner_B6D5B D02 --passC:-DNimMainModule=NimMainModule_B6D5BD02 --passC:-DPreMain=PreMain_B6D5BD02 --passC:-D PreMainInner=PreMainInner_B6D5BD02 -o:build/macosx/x86_64/release/libfoo.a src/foo.nim [ 66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` ## 动态库程序 {#shared-library} ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```sh $ xmake -rv [ 33%]: linking.release libfoo.dylib /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :lib --noMain -o:build/macosx/x86_64/release/libfoo.dylib src/foo.nim [ 66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` ## C 代码混合编译 {#mix-c} ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/*.c") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ## Nimble 依赖包集成 {#nimble-package} 完整例子见:[Nimble Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/nimble_package) ```lua add_rules("mode.debug", "mode.release") add_requires("nimble::zip >0.3") target("test") set_kind("binary") add_files("src/main.nim") add_packages("nimble::zip") ``` ```nim [main.nim] import zip/zlib echo zlibVersion() ``` ## Native 依赖包集成 {#native-package} 完整例子见:[Native Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/native_package) ```lua add_rules("mode.debug", "mode.release") add_requires("zlib") target("test") set_kind("binary") add_files("src/main.nim") add_packages("zlib") ``` main.nim ```nim [main.nim] proc zlibVersion(): cstring {.cdecl, importc} echo zlibVersion() ``` --- --- url: /zh/examples/other-languages/objc.md --- ## Console 程序 {#console} 创建空工程: ```sh $ xmake create -l objc -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.m") ``` 更多例子见:[Objc Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/objc++) ## App 应用程序 {#application} 用于生成\*.app/\*.ipa应用程序,同时支持iOS/MacOS。 ```lua target("test") add_rules("xcode.application") add_files("src/*.m", "src/**.storyboard", "src/*.xcassets") add_files("src/Info.plist") ``` ::: tip 注意 2.5.7 之后,可以支持直接添加 `*.metal` 文件,xmake 会自动生成 default.metallib 提供给应用程序加载使用。 ::: ### 创建工程 {#create-project} 我们也可以通过模板工程快速创建: ```sh $ xmake create -t xcode.macapp -l objc test $ xmake create -t xcode.iosapp -l objc test ``` ### 编译 {#build} ```sh $ xmake f -p [iphoneos|macosx] $ xmake [ 18%]: compiling.xcode.release src/Assets.xcassets [ 27%]: processing.xcode.release src/Info.plist [ 72%]: compiling.xcode.release src/Base.lproj/Main.storyboard [ 81%]: compiling.xcode.release src/Base.lproj/LaunchScreen.storyboard [ 45%]: cache compiling.release src/ViewController.m [ 63%]: cache compiling.release src/AppDelegate.m [ 54%]: cache compiling.release src/SceneDelegate.m [ 36%]: cache compiling.release src/main.m [ 90%]: linking.release test [100%]: generating.xcode.release test.app [100%]: build ok! ``` ### 配置签名 {#configure-sign} 对于iOS程序,默认会检测系统先用可用签名来签名app,当然我们也可以手动指定其他签名证书: ```sh $ xmake f -p iphoneos --xcode_codesign_identity='Apple Development: xxx@gmail.com (T3NA4MRVPU)' --xcode_mobile_provision='iOS Team Provisioning Profile: org.tboox.test --xcode_bundle_identifier=org.tboox.test' $ xmake ``` 如果每次这么配置签名觉得繁琐的话,可以设置到`xmake global`全局配置中,也可以在xmake.lua中对每个target单独设置: ```lua target("test") add_rules("xcode.application") add_files("src/*.m", "src/**.storyboard", "src/*.xcassets") add_files("src/Info.plist") add_values("xcode.bundle_identifier", "org.tboox.test") add_values("xcode.codesign_identity", "Apple Development: xxx@gmail.com (T3NA4MRVPU)") add_values("xcode.mobile_provision", "iOS Team Provisioning Profile: org.tboox.test") ``` 那如何知道我们需要的签名配置呢?一种就是在xcode里面查看,另外xmake也提供了一些辅助工具可以dump出当前可用的所有签名配置: ```sh $ xmake l private.tools.codesign.dump ==================================== codesign identities ==================================== { "Apple Development: waruqi@gmail.com (T3NA4MRVPU)" = "AF73C231A0C35335B72761BD3759694739D34EB1" } ===================================== mobile provisions ===================================== { "iOS Team Provisioning Profile: org.tboox.test" = " AppIDName XC org tboox test5 ApplicationIdentifierPrefix 43AAQM58X3 ... ``` 我们也提供了其他辅助工具来对已有的ipa/app程序进行重签名,例如: ```sh $ xmake l utils.ipa.resign test.ipa|test.app [codesign_identity] [mobile_provision] [bundle_identifier] ``` 其中,后面的签名参数都是可选的,如果没设置,那么默认会探测使用一个有效的签名: ```sh $ xmake l utils.ipa.resign test.ipa $ xmake l utils.ipa.resign test.app "Apple Development: waruqi@gmail.com (T3NA4MRVPU)" $ xmake l utils.ipa.resign test.ipa "Apple Development: waruqi@gmail.com (T3NA4MRVPU)" iOS Team Provisioning Profile: org.tboox.test" org.tboox.test ``` ### 运行应用程序 {#run-program} 目前仅支持运行 macOS 程序: ```sh $ xmake run ``` 效果如下: ![](/assets/img/guide/macapp.png) ### 生成程序包 {#package} 如果是 iOS 程序会生成ipa安装包,如果是macos会生成dmg包(dmg包生成暂时还在开发中)。 ```sh $ xmake package output: build/iphoneos/release/arm64/test.ipa package ok! ``` 我们也提供了辅助工具,来对指定app程序进行打包: ```sh $ xmake l utils.ipa.package test.app output.ipa [iconfile.png] ``` ### 安装 {#install} 如果是iOS程序会安装ipa到设备,如果是macos会安装app到/Applications目录。 ```sh $ xmake install ``` 我们也提供了辅助工具,来对指定ipa/app程序安装到设备: ```sh $ xmake l utils.ipa.install test.app $ xmake l utils.ipa.install test.ipa ``` ### 卸载 {#uninstall} ::: tip 注意 目前仅支持macos程序卸载 ::: ```sh $ xmake uninstall ``` ## Framework 库程序 {#Framework} ```lua target("test") add_rules("xcode.framework") add_files("src/*.m") add_files("src/Info.plist") ``` 我们也可以通过模板工程快速创建: ```sh $ xmake create -t xcode.framework -l objc test ``` 另外,xmake v2.3.9 以上版本,xmake 还提供了带有 framework 库使用的完整 iosapp/macapp 空工程模板,可以完整体验 framework 的编译,依赖使用以及集成到 app 应用程序中。 同时,如果我们开启了模拟器,xmake 可以支持直接 `xmake install` 和 `xmake run` 将 app 安装到模拟器并加载运行。 ```sh $ xmake create -t xcode.iosapp_with_framework -l objc testapp $ cd testapp $ xmake f -p iphoneos -a x86_64 $ xmake $ xmake install $ xmake run ``` ## Bundle 程序 {#bundle} ```lua target("test") add_rules("xcode.bundle") add_files("src/*.m") add_files("src/Info.plist") ``` 我们也可以通过模板工程快速创建: ```sh $ xmake create -t xcode.bundle -l objc test ``` --- --- url: /zh/examples/other-languages/pascal.md --- 2.5.8 之后,我们能够支持构建 Pascal 程序,相关 issues 见:[#388](https://github.com/xmake-io/xmake/issues/388) ## 控制台程序 {#console} ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.pas") ``` ## 动态库程序 {#shared-library} ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.pas") target("test") set_kind("binary") add_deps("foo") add_files("src/main.pas") ``` 更多例子:[Pascal examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/pascal) --- --- url: /zh/examples/other-languages/swift.md --- 创建空工程: ```sh $ xmake create -l swift -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.swift") ``` 更多例子见:[Swift Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/swift) --- --- url: /zh/examples/other-languages/vala.md --- 2.5.7 之后开始支持构建 Vala 程序,我们需要应用 `add_rules("vala")` 规则,并且 glib 包是必须的。 相关 issues: [#1618](https://github.com/xmake-io/xmake/issues/1618) `add_values("vala.packages")` 用于告诉 valac,项目需要哪些包,它会引入相关包的 vala api,但是包的依赖集成,还是需要通过 `add_requires("lua")` 下载集成。 ## 控制台程序 {#console} ```lua add_rules("mode.release", "mode.debug") add_requires("lua", "glib") target("test") set_kind("binary") add_rules("vala") add_files("src/*.vala") add_packages("lua", "glib") add_values("vala.packages", "lua") ``` ## 静态库程序 {#static-library} v2.5.8 之后,我们继续支持构建库程序,能够通过 `add_values("vala.header", "mymath.h")` 设置导出的接口头文件名,通过 `add_values("vala.vapi", "mymath-1.0.vapi")` 设置导出的 vapi 文件名。 ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("static") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` ## 动态库程序 {#shared-library} ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("shared") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` 更多例子:[Vala examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/vala) --- --- url: /zh/examples/other-languages/zig.md --- 创建空工程: ```sh $ xmake create -l zig -t console test ``` ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.zig") ``` 更多例子见:[Zig Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/zig) --- --- url: /zh.md --- --- --- url: /zh/posts/cross-platform-development.md --- title: 使用 Xmake 构建跨平台应用 tags: \[跨平台, 教程, cpp] date: 2024-01-10 author: Ruki outline: deep ------------- 构建能在多个平台上运行的应用程序可能具有挑战性,但 Xmake 让这一切变得简单得多。在本文中,我们将探索如何使用 Xmake 构建跨平台应用程序。 ## 为什么跨平台开发很重要 在当今多样化的计算环境中,用户期望您的应用程序能在他们偏好的平台上运行。无论是 Windows、macOS 还是 Linux,Xmake 都提供了从单一代码库为所有平台构建应用所需的工具。 ## 设置跨平台构建 ### 基本配置 以下是如何配置 Xmake 进行跨平台构建的简单示例: ```lua add_rules("mode.debug", "mode.release") target("myapp") set_kind("binary") add_files("src/*.cpp") add_packages("fmt", "spdlog") -- 平台特定配置 if is_plat("windows") then add_defines("WIN32") add_files("src/win/*.cpp") elseif is_plat("macosx") then add_defines("MACOS") add_files("src/mac/*.cpp") elseif is_plat("linux") then add_defines("LINUX") add_files("src/linux/*.cpp") end ``` ### 交叉编译 Xmake 还支持嵌入式系统的交叉编译: ```lua target("embedded_app") set_kind("binary") add_files("src/*.c") -- 为 ARM 交叉编译 set_plat("cross") set_arch("arm") set_toolchain("gcc") add_toolchains("arm-none-eabi-gcc") ``` ## 最佳实践 1. **使用平台抽象**:为系统调用创建平台特定的抽象 2. **在所有平台上测试**:设置 CI/CD 在所有目标平台上进行测试 3. **使用条件编译**:使用 Xmake 的平台检测进行平台特定代码 4. **包依赖管理**:使用 Xmake 的包管理进行跨平台依赖管理 ## 结论 使用 Xmake,跨平台开发变得更加容易管理。统一的构建系统和优秀的包管理使得从单一配置针对多个平台变得简单。 更多信息,请查看我们的[交叉编译指南](https://xmake.io/zh/)。 --- --- url: /posts/variables-usage.md --- ## Built-in Variables Embedded in strings, for example: ```lua set_objectdir("$(buildir)/.objs") ``` Among them, `$(buildir)` is a built-in variable. These automatically change as the configuration changes with each `xmake config`. Currently supported variables are as follows: * `$(buildir)`: Compilation output directory, can be modified via: `xmake f -o /tmp` * `$(projectdir)`: Project main directory, can be modified via: `xmake f -P ./project` * `$(os)`: Operating system of the compilation target * `$(plat)`: Platform where the compilation target is located, can be modified via: `xmake f -p android` * `$(mode)`: Compilation mode: debug, release, profile, can be modified via: `xmake f -m debug` * `$(arch)`: Architecture of the compilation target, can be modified via: `xmake f -a armv7` Note: All parameter options configured through `xmake f/config` can be accessed through built-in variables, for example on android: ```lua xmake f -p android --ndk=/xxxx ``` Then `$(ndk)` is an accessible variable and changes as the configuration changes, but this cannot be used on non-android platforms. All other configuration-related variables can be viewed through the following command: ```lua xmake f --help ``` ## External Variables External variables are simple, they are lua variable operations. Since `xmake.lua` itself is a lua script, all features of lua can be directly used, so it can be used like this: ```lua local root = "/tmp" set_objectdir(root .. ".objs") ``` Just use lua's string variable concatenation syntax. Isn't it simple? --- --- url: /posts/custom-rule.md --- After version 2.1.9, xmake not only natively supports building multiple language files, but also allows users to implement complex unknown file builds through custom build rules. For specific usage instructions, please refer to the relevant documentation: [Rule Usage Manual](https://xmake.io/) #### General Rules We can extend build support for other files by pre-setting the file extensions supported by rules: ```lua -- Define a build rule for markdown files 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 test target support markdown file build rules add_rules("markdown") -- Add markdown file builds add_files("src/*.md") add_files("src/*.markdown") ``` We can also specify some scattered other files to be processed as markdown rules: ```lua target("test") -- ... add_files("src/test/*.md.in", {rule = "markdown"}) ``` Note: Rules specified through `add_files("*.md", {rule = "markdown"})` have higher priority than rules set by `add_rules("markdown")`. #### Dependent Builds We can also implement cascading builds of rules. For example, after building the man rule, continue to call the markdown rule to implement cascading builds: ```lua rule("man") add_imports("core.project.rule") on_build(function (target, sourcefile) rule.build("markdown", target, sourcefile) end) ``` Among them, `add_imports` is used to pre-import extension modules, which can be directly used in multiple custom scripts. For specific instructions, see: [add\_imports Documentation](https://xmake.io/) #### Multi-file Builds For some files, you need to support multi-file builds to generate a single object mode, which can be implemented through `on_build_all`: ```lua rule("man") on_build_all(function (target, sourcefiles) -- build some source files for _, sourcefile in ipairs(sourcefiles) do -- ... end end) target("test") -- ... add_files("src/test/*.doc.in", {rule = "man"}) ``` #### Clean and Install We can use `on_clean`, `on_install` to implement custom rule cleanup and installation logic, processing one source file at a time, for example: ```lua rule("markdown") on_build(function (target, sourcefile) os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) end) on_clean(function (target, sourcefile) os.rm(path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) end) on_install(function (target, sourcefile) import("core.base.option") os.cp(path.join(target:targetdir(), path.basename(sourcefile) .. ".html"), option.get("outputdir")) end) ``` Finally, here's a complete example for reference: [Rule Usage Example](https://github.com/xmake-io/xmake/issues/149) --- --- url: /guide/package-management/using-local-packages.md --- # Using Local Packages ## Generate a local package After version 2.5.5, we have provided a new local package packaging solution that will seamlessly integrate with `add_requires` and `add_packages`. We can execute the `xmake package` command to generate the default new version of the packaging format. ```sh $ xmake package package(foo): build/packages/f/foo generated ``` It will generate the file `build/packages/f/foo/xmake.lua` with the following content: ```lua package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") on_load(function (package) package:set("installdir", path.join(os.scriptdir(), package:plat(), package:arch(), package:mode())) end) on_fetch(function (package) local result = {} result.links = "foo" result.linkdirs = package:installdir("lib") result.includedirs = package:installdir("include") return result end) ``` In fact, it uses `package()` to define and describe local packages, just like remote packages. The generated directory structure is as follows: ```sh $ tree build/packages/f/foo/ build/packages/f/foo/ ├── macosx │   └── x86_64 │   └── release │   ├── include │   │   └── foo.h │   └── lib │   └── libfoo.a └── xmake.lua ``` ## Using generated package We can also use the `add_requires`/`add_repositories` interface to seamlessly integrate this package. ```lua add_rules("mode.debug", "mode.release") add_repositories("local-repo build") add_requires("foo") target("bar") set_kind("binary") add_files("src/*.cpp") add_packages("foo") ``` Among them, the add\_repositories configuration specifies the repository root directory of the local package, and then this package can be referenced through `add_requires`. In addition, the generated local package has another feature, which is to support `target/add_deps`, which automatically associates the dependencies of multiple packages, and automatically connects all dependency links during integration. Here is the complete [test example](https://github.com/xmake-io/xmake/blob/dev/tests/actions/package/localpkg/test.lua). ```sh "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/bar build/.objs/bar/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version -min=10.15 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk -stdlib=libc++ -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/f/foo/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/s/sub/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/a/add/macosx/x86_64/release/lib -Wl,-x -lfoo -lsub -ladd -lz ``` ## Using package from CMake Now CMake is the de facto standard, so the find\_package provided by CMake can already find a large number of libraries and modules. We fully reuse this part of CMake's ecosystem to expand xmake's integration of packages. We can use `find_package("cmake::xxx")` to find some packages with CMake. Xmake will automatically generate a CMake script to call CMake's find\_package to find some packages and get the build information. E.g: ```sh $ xmake l find_package cmake::ZLIB { links = { "z" }, includedirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/include" }, linkdirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/lib" } } $ xmake l find_package cmake::LibXml2 { links = { "xml2" }, includedirs = { "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/libxml2" }, linkdirs = { "/usr/lib" } } ``` If we integrate and find CMake dependent packages in the xmake.lua project configuration, we usually don't need to use find\_package directly, and we can use a more general and simple package integration method. ```lua add_requires("cmake::ZLIB", {alias = "zlib", system = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` We specify `system = true` to tell xmake to force CMake to find the package from the system. If it cannot be found, the installation logic will not be followed, because CMake does not provide the installation function of package managers such as vcpkg/conan. Only the package search feature is provided. ### Specify version ```lua add_requires("cmake::OpenCV 4.1.1", {system = true}) ``` ### Specified components ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}})) ``` ### Default switch ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}}) ``` It is equivalent to predefining some configurations in CMakeLists.txt before calling find\_package internally to find the package, to control the find\_package search strategy and status. ``` set(Boost_USE_STATIC_LIB ON) - will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system) ``` ### Set environment variables ```lua add_requires("cmake::OpenCV", {system = true, configs = {envs = {CMAKE_PREFIX_PATH = "xxx"}}}) ``` ### Specify custom FindFoo.cmake module script directory mydir/cmake\_modules/FindFoo.cmake ```lua add_requires("cmake::Foo", {system = true, configs = {moduledirs = "mydir/cmake_modules"}}) ``` Related issues: [#1632](https://github.com/xmake-io/xmake/issues/1632) ### Specifying links For CMake packages, we have added the `link_libraries` configuration option to allow users to customize the configuration of package dependencies and even support for target links when looking to use CMake packages. ```lua add_requires("cmake::xxx", {configs = {link_libraries = {"abc::lib1", "abc::lib2"}}}) ``` xmake automatically appends the following configuration to improve the extraction of linked libraries when looking for CMake packages. ```cmake target_link_libraries(test PRIVATE ABC::lib1 ABC::lib2) ``` ### Specify the search mode In addition, we add the following search mode configuration. ```lua add_requires("cmake::xxx", {configs = {search_mode = "config"}}) add_requires("cmake::xxx", {configs = {search_mode = "module"}}) add_requires("cmake::xxx") -- both ``` Specify config search mode, for example, to tell CMake to look for packages from `XXXConfig.cmake`. xmake will automatically append the following configuration internally when it looks for CMake packages. ```cmake find_package(ABC CONFIG REQUIRED) ``` --- --- url: /guide/package-management/using-official-packages.md --- # Using Official Packages This has been initially supported after version 2.2.2. The usage is much simpler; just set the corresponding dependency package, for example: ```lua add_requires("tbox 1.6.*", "libpng ~1.16", "zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("tbox", "libpng", "zlib") ``` The above `add_requires` is used to describe the dependencies required by the current project, and `add_packages` is used to apply dependencies to the test target. Only these settings will automatically add links, linkdirs, includedirs, etc. Then directly compile: ```sh $ xmake ``` xmake will remotely pull the relevant source package, then automatically compile and install, finally compile the project, and link the dependency package. The specific effect is shown in the following figure: 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-repository management support such as official package repository, self-built private repository, project built-in repository, etc. * Cross-platform package compilation integration support (packages of different platforms and different architectures can be installed at the same time, fast switching and use) * Debug dependency package support, source code debugging ## Dependency Package Processing Mechanism Here we briefly introduce the processing mechanism of the entire dependency package: 1. Priority check for the current system directory, whether there is a specified package under the third-party package management. If there is a matching package, then you do not need to download and install (of course you can also set the system package) 2. Retrieve the package matching the corresponding version, then download, compile, and install (Note: installed in a specific xmake directory, will not interfere with the system library environment) 3. Compile the project, and finally automatically link the enabled dependencies ## Semantic Version Settings Xmake's dependency package management fully supports semantic version selection, for example: "~1.6.1". For a detailed description of the semantic version, see: Some semantic versions are written: ```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 current version of the dependency package, then we can write directly: ```lua add_requires("tbox", "libpng", "zlib") ``` This will use the latest version of the package known, or the source code compiled by 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") ``` ## Extra Package Information Settings ### Optional Package Settings If the specified dependency package is not supported by the current platform, or if the compilation and installation fails, then xmake will compile the 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}) ``` ### Disable System Library 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 or the library provided by third-party package management, then you can set: ```lua add_requires("tbox", {system = false}) ``` ### Using the debug version of the package 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 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) ``` ### Passing additional compilation information to the package Some packages have various compile options at compile time, and we can pass them in, provided the package itself supports it: ```lua add_requires("tbox", {configs = {small=true}}) ``` Pass `--small=true` to the tbox package so that compiling the installed tbox package is enabled. We can get a list of all configurable parameters and descriptions of the specified package by executing `xmake require --info tbox` in the project directory. such as: ```sh xmake require --info spdlog require(spdlog): -> requires: -> plat: macosx -> arch: x86_64 -> configs: -> header_only: true -> shared: false -> vs_runtime: MT -> debug: false -> fmt_external: true -> noexcept: false -> configs: -> header_only: Use header only (default: true) -> fmt_external: Use external fmt library instead of bundled (default: false) -> noexcept: Compile with -fno-exceptions. Call abort() on any spdlog exceptions (default: false) -> configs (builtin): -> debug: Enable debug symbols. (default: false) -> shared: Enable shared library. (default: false) -> cflags: Set the C compiler flags. -> cxflags: Set the C/C++ compiler flags. -> cxxflags: Set the C++ compiler flags. -> asflags: Set the assembler flags. -> vs_runtime: Set vs compiler runtime. (default: MT) -> values: {"MT","MD"} ``` Among them, configs is the configurable parameters provided by the spdlog package itself, and the configs part with builtin below is the built-in configuration parameters that all packages will have. The top required section is the current configuration value of the project. ::: tip NOTE `vs_runtime` is the setting for vs runtime under msvc. In v2.2.9, it also supports automatic inheritance of all static dependencies. That is to say, if spdlog is set to MD, then the fmt package it depends on will also inherit automatically. Set the MD. ::: It can be seen that we have been able to customize the required packages very conveniently, but each package may have a lot of dependencies. If these dependencies are also customized, what should I do? ### Install any version of the package By default, `add_requires("zlib >1.2.x")` can only select the package version that exists in the `xmake-repo` repository, because each version of the package will have a sha256 check value. Use To check the integrity of the package. Therefore, there is no check value for packages of unknown version, and xmake will not let you choose to use it by default, which is not safe. What if the package version we need cannot be selected for use? There are two ways, one is to submit a pr to [xmake-repo](https://github.com/xmake-io/xmake-repo), add the new version of the specified package and the corresponding sha256, for example: ```lua package("zlib") add_versions("1.2.10", "8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017") add_versions("1.2.11", "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1") ``` In addition, there is another way that the user passes `{verify = false}` configuration to `add_requires` to force the file integrity check of the package to be ignored, so that the sha256 value is not needed, so any version of the package can be installed. Of course, there will be a certain degree of security risk and the risk of incomplete packages, which requires users to choose and evaluate. ```lua add_requires("zlib 1.2.11", {verify = false}) ``` ### Disable external header file search path By default, packages installed through `add_requires` will use `-isystem` or `/external:I` to refer to the header file path inside, which can usually avoid the unmodifiable warning messages introduced by some package header files. However, we can still disable external header files by setting `{external = false}` and switch back to the use of `-I`. The compilation flags for external header files are enabled by default as follows: ```sh -isystem /Users/ruki/.xmake/packages/z/zlib/1.2.11/d639b7d6e3244216b403b39df5101abf/include ``` Manually turn off the compilation flags of external header files as follows: ```lua add_requires("zlib 1.x", {external = false}) ``` ```sh -I /Users/ruki/.xmake/packages/z/zlib/1.2.11/d639b7d6e3244216b403b39df5101abf/include ``` ## Using self-built private package repository 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 the repository with the following command: ```sh $ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git ``` Starting with v2.2.3, support for adding repos for specified branches, for example: ```sh $ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git dev ``` Or we write directly in xmake.lua: ```lua add_repositories("my-repo git@github.com:myrepo/xmake-repo.git") add_repositories("my-repo git@github.com:myrepo/xmake-repo.git dev") ``` 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. We can even build a package without directly building a package description into the project xmake.lua, which is useful for relying on one or two packages, for example: ```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") ``` ## Dependent package lock and upgrade After v2.5.7, version locks of dependent packages have been added, similar to npm's package.lock and cargo's cargo.lock. For example, if we quote some packages, by default, if the version is not specified, xmake will automatically pull the latest version of the package for integrated use each time, for example: ```lua add_requires("zlib") ``` However, if the upstream package warehouse is updated and changed, for example, zlib adds a new version 1.2.11, or the installation script is changed, the user's dependent packages will change. This can easily lead to some projects that were originally compiled and passed, and there may be some unstable factors due to changes in dependent packages, and the compilation may fail, etc. In order to ensure that the package used by the user's project is fixed each time, we can enable the package dependency lock through the following configuration. ```lua set_policy("package.requires_lock", true) ``` This is a global setting and must be set to the global root scope. If enabled, xmake will automatically generate a `xmake-requires.lock` configuration file after executing package pull. It contains all the packages that the project depends on, as well as the current package version and other information. ```lua { __meta__ = { version = "1.0" }, ["macosx|x86_64"] = { ["cmake#31fecfc4"] = { repo = { branch = "master", commit = "4498f11267de5112199152ab030ed139c985ad5a", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.21.0" }, ["glfw#31fecfc4"] = { repo = { branch = "master", commit = "eda7adee81bac151f87c507030cc0dd8ab299462", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.3.4" }, ["opengl#31fecfc4"] = { repo = { branch = "master", commit = "94d2eee1f466092e04c5cf1e4ecc8c8883c1d0eb", url = "https://github.com/xmake-io/xmake-repo.git" } } } } ``` Of course, we can also execute the following command to force the upgrade package to the latest version. ```sh $ xmake require --upgrade upgrading packages .. zlib: 1.2.10 -> 1.2.11 1 package is upgraded! ``` --- --- url: /guide/package-management/using-packages-in-cmake.md --- # Using packages in CMake CMake wrapper for [Xrepo](https://xrepo.xmake.io/) C and C++ package manager. This allows you to use CMake to build your project, while using Xrepo to manage dependent packages. This project is partially inspired by [cmake-conan](https://github.com/conan-io/cmake-conan). Example use cases for this project: * Existing CMake projects which want to use Xrepo to manage packages. * New projects which have to use CMake, but want to use Xrepo to manage packages. ## APIs ### xrepo\_package [xrepo.cmake](https://github.com/xmake-io/xrepo-cmake/blob/main/xrepo.cmake) provides `xrepo_package` function to manage packages. ```cmake xrepo_package( "foo 1.2.3" [CONFIGS feature1=true,feature2=false] [CONFIGS path/to/script.lua] [DEPS] [MODE debug|release] [ALIAS aliasname] [OUTPUT verbose|diagnosis|quiet] [DIRECTORY_SCOPE] ) ``` Some of the function arguments correspond directly to Xrepo command options. `xrepo_package` adds the package install directory to `CMAKE_PREFIX_PATH`. So `find_package` can be used. If `CMAKE_MINIMUM_REQUIRED_VERSION` >= 3.1, CMake `PkgConfig` will also search for pkgconfig files under package install directories. After calling `xrepo_package(foo)`, there are three ways to use `foo` package: 1. Call `find_package(foo)` if package provides cmake config-files. * Refer to CMake [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) documentation for more details. 2. If the package does not provide cmake config files or find modules * Following variables can be used to use the package (variable names following CMake find modules [standard variable names](https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names)) * `foo_INCLUDE_DIRS` * `foo_LIBRARY_DIRS` * `foo_LIBRARIES` * `foo_DEFINITIONS` * If `DIRECTORY_SCOPE` is specified, `xrepo_package` will run following code ```cmake include_directories(${foo_INCLUDE_DIRS}) link_directories(${foo_LIBRARY_DIRS}) ``` 3. Use `xrepo_target_packages`. Please refer to following section. Note `CONFIGS path/to/script.lua` is for fine control over package configs. For example: * Exclude packages on system. * Override dependent packages' default configs, e.g. set `shared=true`. If `DEPS` is specified, all dependent libraries will add to `CMAKE_PREFIX_PATH`, along with include, libraries being included in the four variables. ### xrepo\_target\_packages Add package includedirs and links/linkdirs to the given target. ```cmake xrepo_target_packages( target [NO_LINK_LIBRARIES] [PRIVATE|PUBLIC|INTERFACE] package1 package2 ... ) ``` * `NO_LINK_LIBRARIES` * In case a package provides multiple libs and user need to select which one to link, pass `NO_LINK_LIBRARIES` to disable calling `target_link_libraries`. User should call `target_link_libraries` to setup correct library linking. * `PRIVATE|PUBLIC|INTERFACE` * The default is not specifying a propagation control keyword when calling `target_include_libraries`, `target_link_libraries`, etc., because there's no default choice on this in CMake. * Refer to this [Stack Overflow answer](https://stackoverflow.com/a/26038443) for differences. ## Use package from official repository Xrepo official repository: [xmake-repo](https://github.com/xmake-io/xmake-repo) Here's an example `CMakeLists.txt` that uses `gflags` package version 2.2.2 managed by Xrepo. ### Integrate xrepo.cmake ```cmake cmake_minimum_required(VERSION 3.13.0) project(foo) # Download xrepo.cmake if not exists in build directory. if(NOT EXISTS "${CMAKE_BINARY_DIR}/xrepo.cmake") message(STATUS "Downloading xrepo.cmake from https://github.com/xmake-io/xrepo-cmake/") # mirror https://cdn.jsdelivr.net/gh/xmake-io/xrepo-cmake@main/xrepo.cmake file(DOWNLOAD "https://raw.githubusercontent.com/xmake-io/xrepo-cmake/main/xrepo.cmake" "${CMAKE_BINARY_DIR}/xrepo.cmake" TLS_VERIFY ON) endif() # Include xrepo.cmake so we can use xrepo_package function. include(${CMAKE_BINARY_DIR}/xrepo.cmake) ``` ### Add basic packages ```cmake xrepo_package("zlib") add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) xrepo_target_packages(example-bin zlib) ``` ### Add packages with configs ```cmake xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true") add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) xrepo_target_packages(example-bin gflags) ``` ### Add packages with cmake modules ```cmake xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true") # `xrepo_package` add gflags install directory to CMAKE_PREFIX_PATH. # As gflags provides CMake config-files, we can now call `find_package` to find # the gflags package. # Refer to https://cmake.org/cmake/help/latest/command/find_package.html#search-modes find_package(gflags CONFIG COMPONENTS shared) add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) target_link_libraries(example-bin gflags) ``` ### Add custom packages We can also add custom packages in our project. ```cmake set(XREPO_XMAKEFILE ${CMAKE_CURRENT_SOURCE_DIR}/packages/xmake.lua) xrepo_package("myzlib") add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) xrepo_target_packages(example-bin myzlib) ``` Define myzlib package in packages/xmake.lua ```lua package("myzlib") set_homepage("http://www.zlib.net") set_description("A Massively Spiffy Yet Delicately Unobtrusive Compression Library") set_urls("http://zlib.net/zlib-$(version).tar.gz", "https://downloads.sourceforge.net/project/libpng/zlib/$(version)/zlib-$(version).tar.gz") add_versions("1.2.10", "8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017") on_install(function (package) -- TODO end) on_test(function (package) assert(package:has_cfuncs("inflate", {includes = "zlib.h"})) end) ``` We can write a custom package in xmake.lua. Please refer to [Define Xrepo package](/guide/package-management/package-distribution.html#define-package-configuration). ## Options and variables for `xrepo.cmake` Following options can be specified with `cmake -D=`. Or use `set(var value)` in `CMakeLists.txt`. * `XMAKE_CMD`: string, defaults to empty string * Specify path to `xmake` command. Use this option if `xmake` is not installed in standard location and can't be detected automatically. * `XREPO_PACKAGE_VERBOSE`: `[ON|OFF]` * Enable verbose output for Xrepo Packages. * `XREPO_BOOTSTRAP_XMAKE`: `[ON|OFF]` * If `ON`, `xrepo.cmake` will install `xmake` if it is not found. * `XREPO_PACKAGE_DISABLE`: `[ON|OFF]` * Set this to `ON` to disable `xrepo_package` function. * If setting this variable in `CMakeLists.txt`, please set it before including `xrepo.cmake`. ## Switching compiler and cross compilation Following variables controll cross compilation. Note: to specify a different compiler other than the default one on system, platform must be set to "cross". * `XREPO_TOOLCHAIN`: string, defaults to empty string * Specify toolchain name. Run `xmake show -l toolchains` to see available toolchains. * `XREPO_PLATFORM`: string, defaults to empty string * Specify platform name. If `XREPO_TOOLCHAIN` is specified and this is not, `XREPO_PLATFORM` will be set to `cross`. * `XREPO_ARCH`: string, defaults to empty string * Specify architecture name. * `XREPO_XMAKEFILE`: string, defaults to empty string * Specify Xmake script file of Xrepo package. ## Use package from 3rd repository In addition to installing packages from officially maintained repository, Xrepo can also install packages from third-party package managers such as vcpkg/conan/conda/pacman/homebrew/apt/dub/cargo. For the use of the command line, we can refer to the documentation: [Xrepo command usage](/guide/package-management/xrepo-cli). We can also use it directly in cmake to install packages from third-party repositories, just add the repository name as a namespace. e.g. `vcpkg::zlib`, `conan::pcre2` ### Conan ```cmake xrepo_package("conan::gflags/2.2.2") ``` ### Conda ```cmake xrepo_package("conda::gflags 2.2.2") ``` ### Vcpkg ```cmake xrepo_package("vcpkg::gflags") ``` ### Homebrew ```cmake xrepo_package("brew::gflags") ``` ## How does it work? [xrepo.cmake](https://github.com/xmake-io/xrepo-cmake/blob/main/xrepo.cmake) module basically does the following tasks: * Call `xrepo install` to ensure specific package is installed. * Call `xrepo fetch` to get package information and setup various variables for using the installed package in CMake. The following section is a short introduction to using Xrepo. It helps to understand how `xrepo.cmake` works and how to specify some of the options in `xrepo_package`. ## Xrepo workflow Assmuing [Xmake](https://github.com/xmake-io/xmake/) is installed. Suppose we want to use `gflags` packages. First, search for `gflags` package in Xrepo. ``` $ xrepo search gflags The package names: gflags: -> gflags-v2.2.2: The gflags package contains a C++ library that implements commandline flags processing. (in builtin-repo) ``` It's already in Xrepo, so we can use it. If it's not in Xrepo, we can create it in [self-built repositories](/guide/package-management/xrepo-cli.html#support-distributed-repository). Let's see what configs are available for the package before using it: ``` $ xrepo info gflags ... -> configs: -> mt: Build the multi-threaded gflags library. (default: false) -> configs (builtin): -> debug: Enable debug symbols. (default: false) -> shared: Build shared library. (default: false) -> pic: Enable the position independent code. (default: true) ... ``` Suppose we want to use multi-threaded gflags shared library. We can install the package with following command: ``` xrepo install --mode=release --configs='mt=true,shared=true' 'gflags 2.2.2' ``` Only the first call to the above command will compile and install the package. To speed up cmake configuration, `xrepo` command will only be executed when the package is not installed or `xrepo_package` parameters have changed. After package installation, because we are using CMake instead of Xmake, we have to get package installation information by ourself. `xrepo fetch` command does exactly this: ``` xrepo fetch --json --mode=release --configs='mt=true,shared=true' 'gflags 2.2.2' ``` The above command will print out package's include, library directory along with other information. `xrepo_package` uses these information to setup variables to use the specified package. For CMake 3.19 and later which has JSON support, `xrepo_package` parses the JSON output. For previous version of CMake, `xrepo_package` uses only the `--cflags` option to get package include directory. Library and cmake module directory are infered from that directory, so it maybe unreliable to detect the correct paths. --- --- url: /guide/package-management/using-source-code-packages.md --- # Using Source Code Packages ## Integrate CMake source library After version 2.5.8, we can also directly integrate the source library with CMakeLists.txt in our project through the package mode of xmake, instead of downloading and installing it remotely. Related issues: [#1714](https://github.com/xmake-io/xmake/issues/1714) For example, we have the following project structure: ``` ├── foo │ ├── CMakeLists.txt │ └── src │ ├── foo.c │ └── foo.h ├── src │ └── main.c ├── test.lua └── xmake.lua ``` The foo directory is a static library maintained by CMake, and the root directory is maintained by xmake. We can define the `package("foo")` package in xmake.lua to describe how to build the foo code library. ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` Among them, we set the code directory location of the foo package through `set_sourcedir()`, and then import the auxiliary module of `package.tools.cmake` through import to call CMake to build the code. Xmake will automatically obtain the generated libfoo.a and the corresponding header file. ::: tip NOTE If only the local source code is integrated, we don't need to set additional `add_urls` and `add_versions`. ::: For the configuration description of the package, see: [Package description](/guide/package-management/package-distribution.html#define-package-configuration). After defining the package, we can integrate it with `add_requires("foo")` and `add_packages("foo")`, just like integrating remote packages. In addition, `on_test` is optional. If you want to strictly check whether the package is compiled and installed successfully, you can do some tests in it. For a complete example, see: [Library with CMakeLists](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/library_with_cmakelists) ## Integrate Meson source library xmake supports the integration of more third-party source code libraries maintained by other build systems, such as Meson. You only need to import and use the `package.tools.meson` auxiliary building module to call Meson to build them. For example, we select a package built with Meson from the xmake-repo repository as an example: ```lua package("harfbuzz") set_sourcedir(path.join(os.scriptdir(), "3rd/harfbuzz")) add_deps("meson") if not is_plat("windows") then add_deps("freetype") end on_load("windows", "linux", "macosx", function (package) if package:config("icu") then package:add("deps", "icu4c") end end) on_install("windows", "linux", "macosx", function (package) local configs = {"-Dtests=disabled", "-Ddocs=disabled", "-Dbenchmark=disabled", "-Dcairo=disabled", "-Dfontconfig=disabled", "-Dglib=disabled", "-Dgobject= disabled"} table.insert(configs, "-Ddefault_library=" .. (package:config("shared") and "shared" or "static")) if package:is_plat("windows") then table.insert(configs, "-Dfreetype=disabled") end import("package.tools.meson").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("hb_buffer_add_utf8", {includes = "harfbuzz/hb.h"})) end) ``` ## Integrate autoconf source library We can also use `package.tools.autoconf` to locally integrate third-party code libraries maintained by autoconf. ```lua package("pcre2") set_sourcedir(path.join(os.scriptdir(), "3rd/pcre2")) add_configs("jit", {description = "Enable jit.", default = true, type = "boolean"}) add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values ​​= {"8", "16", "32"}}) on_load(function (package) local bitwidth = package:config("bitwidth") or "8" package:add("links", "pcre2-" .. bitwidth) package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth) if not package:config("shared") then package:add("defines", "PCRE2_STATIC") end end) on_install("macosx", "linux", "mingw", function (package) local configs = {} table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) if package:debug() then table.insert(configs, "--enable-debug") end if package:config("pic") ~= false then table.insert(configs, "--with-pic") end if package:config("jit") then table.insert(configs, "--enable-jit") end local bitwidth = package:config("bitwidth") or "8" if bitwidth ~= "8" then table.insert(configs, "--disable-pcre2-8") table.insert(configs, "--enable-pcre2-" .. bitwidth) end import("package.tools.autoconf").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("pcre2_compile", {includes = "pcre2.h"})) end) ``` Both `package.tools.autoconf` and `package.tools.cmake` modules can support cross-compilation platforms and toolchains such as mingw/cross/iphoneos/android. Xmake will automatically pass the corresponding toolchain into it, and the user does not need to do anything else. ## Integrate Scons source library We can also use `package.tools.scons` to locally integrate third-party code libraries maintained by SCons. ```lua package("godotcpp") set_sourcedir(path.join(os.scriptdir(), "3rd/godotcpp")) add_deps("scons") add_includedirs("include", "include/core", "include/gen") on_install("linux", "windows", "macosx", "mingw", "cygwin", "iphoneos", "android", "msys", function (package) local configs = {"generate_bindings=yes"} table.insert(configs, "bits=" .. ((package:is_arch("x64") or package:is_arch("x86_64")) and "64" or "32")) if package:is_plat("windows") then io.replace("SConstruct", "/MD", "/" .. package:config("vs_runtime"), {plain = true}) end - this fixes an error on ios and osx (https://godotengine.org/qa/65616/problems-compiling-gdnative-c-example-on-osx) if package:is_plat("macosx", "iphoneos") then io.replace("SConstruct", "-std=c++14", "-std=c++17", {plain = true}) end - fix to use correct ranlib, @see https://github.com/godotengine/godot-cpp/issues/510 if package:is_plat("android") then io.replace("SConstruct", [[env['AR'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ar"]], [[env['AR'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ar" env['RANLIB'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ranlib"]], {plain = true}) end import("package.tools.scons").build(package, configs) os.cp("bin/*." .. (package:is_plat("windows") and "lib" or "a"), package:installdir("lib")) os.cp("include/core/*.hpp", package:installdir("include/core")) os.cp("include/gen/*.hpp", package:installdir("include/gen")) os.cp("godot-headers/android", package:installdir("include")) os.cp("godot-headers/arvr", package:installdir("include")) os.cp("godot-headers/gdnative", package:installdir("include")) os.cp("godot-headers/nativescript", package:installdir("include")) os.cp("godot-headers/net", package:installdir("include")) os.cp("godot-headers/pluginscript", package:installdir("include")) os.cp("godot-headers/videodecoder", package:installdir("include")) os.cp("godot-headers/*.h", package:installdir("include")) end) ``` ## Integrated makefile source library ### Use Nmake We can also use `package.tools.nmake` to locally integrate third-party code libraries maintained by NMake. `nmake.install` will automatically bind the msvc build environment of the current user to ensure that the user can successfully call nmake.exe, msbuild and cl.exe and other programs. ```lua package("libxml2") set_sourcedir(path.join(os.scriptdir(), "3rd/libxml2")) add_includedirs("include/libxml2") if is_plat("windows") then add_syslinks("wsock32", "ws2_32") end on_load("windows", function (package) if not package:config("shared") then package:add("defines", "LIBXML_STATIC") end end) on_install("windows", function (package) os.cd("win32") os.vrun("cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/%s debug=%s prefix=\"%s\"", package:config("vs_runtime"), package:debug( ) and "yes" or "no", package:installdir()) import("package.tools.nmake").install(package, {"/f", "Makefile.msvc"}) os.tryrm(path.join(package:installdir("lib"), "libxml2_a_dll.lib")) if package:config("shared") then os.tryrm(path.join(package:installdir("lib"), "libxml2_a.lib")) else os.tryrm(path.join(package:installdir("lib"), "libxml2.lib")) os.tryrm(path.join(package:installdir("bin"), "libxml2.dll")) end end) on_test(function (package) assert(package:has_cfuncs("xmlNewNode", {includes = {"libxml/parser.h", "libxml/tree.h"}})) end) ``` ### Use GnuMake We can also use `package.tools.make` to locally integrate third-party code libraries maintained by gnumake. ```lua package("openssl") set_sourcedir(path.join(os.scriptdir(), "3rd/openssl")) add_links("ssl", "crypto") if is_plat("linux", "cross") then add_syslinks("dl") end on_install("linux", "macosx", function (package) - https://wiki.openssl.org/index.php/Compilation_and_Installation#PREFIX_and_OPENSSLDIR os.vrun("./config %s --openssldir=\"%s\" --prefix=\"%s\"", package:debug() and "--debug" or "", package:installdir (), package:installdir()) import("package.tools.make").install(package) end) on_test(function (package) assert(package:has_cfuncs("SSL_new", {includes = "openssl/ssl.h"})) end) ``` ::: tip NOTE We can also directly use `os.vrunv("make", {})` to call the make/gmake program to build the library. ::: ## Integrate GN source library We can also use `package.tools.gn` to natively integrate third-party code maintained by GN. ```lua package("skia") set_sourcedir(path.join(os.scriptdir(), "3rd/openssl")) add_deps("gn", "ninja") add_deps("python", {kind = "binary"}) on_install("linux", "macosx", "windows", function (package) import("package.tools.gn").install(package) end) on_test(function (package) -- TODO end) ``` Here is the complete script example: [Skia with GN](https://github.com/xmake-io/xmake-repo/blob/master/packages/s/skia/xmake.lua) --- --- url: /guide/package-management/using-system-packages.md --- # Using System Packages ## Find using system packages Xmake unifies the use of remote packages and system packages, and all use the `add_requires("zlib")` interface to describe the integration. With the default configuration method, it will first find the library from the system; if not, it will automatically download and install it for integration. And if we just want to find and use the system library and don't want to download it remotely, we can configure it like this: ```lua add_requires("zlib", {system = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` Remote download can be forcibly disabled by `{system = true}`. At this time, it is equivalent to `find_package` of Xmake/CMake, but it is simpler and easier to use, and it is exactly the same as the use of remote packages. By default, if the system library is not found, it will prompt failure. If the package is optional, you can additionally configure the `{optional = true}` option. ```lua add_requires("zlib", {system = true, optional = true}) ``` ## Test command for finding package We can use the following command to quickly detect the package information specified on the system: ```sh $ xmake l find_package x264 {      links = {        "x264"      },      linkdirs = {        "/usr/local/Cellar/x264/r2699/lib"      },      version = "0.148.2699 a5e06b9",      includeirs = {        "/usr/local/Cellar/x264/r2699/include"      } } ``` We can also add a third-party package manager prefix to test: ```sh xmake l find_package conan::OpenSSL/1.0.2g ``` ::: tip NOTE It should be noted that if the find\_package command is executed in the project directory with xmake.lua, there will be a cache. If the search fails, the next lookup will also use the cached result. If you want to force a retest every time, please switch to a non-project directory to execute the above command. ::: --- --- url: /guide/package-management/using-third-party-packages.md --- # Using third-party packages After version 2.2.5, xmake supports dependency libraries in third-party package managers, such as: conan, brew, vcpkg, clib, etc. ## Using 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") ``` ## Using Vcpkg dependency package ```lua add_requires("vcpkg::zlib", "vcpkg::pcre2") target("test") set_kind("binary") add_files("src/*.c") add_packages("vcpkg::zlib", "vcpkg::pcre2") ``` We can also add a package alias name to simplify the use of `add_packages`: ```lua add_requires("vcpkg::zlib", {alias = "zlib"}) add_requires("vcpkg::pcre2", {alias = "pcre2"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "pcre2") ``` If the vcpkg package has optional features, we can also directly use the vcpkg syntax format `packagename[feature1,feature2]` to install the package. e.g: ```lua add_requires("vcpkg::boost[core]") ``` After v2.6.3, xmake supports the new manifest mode of vcpkg, through which we can support version selection of vcpkg packages, for example: ```lua add_requires("vcpkg::zlib 1.2.11") add_requires("vcpkg::fmt >=8.0.1", {configs = {baseline = "50fd3d9957195575849a49fa591e645f1d8e7156"}}) add_requires("vcpkg::libpng", {configs = {features = {"apng"}}}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("vcpkg::zlib", "vcpkg::fmt", "vcpkg::libpng") ``` After v2.6.8, it is also possible to additionally configure private repositories, which is only available in manifest mode. ```lua local registries = { { kind = "git", repository = "https://github.com/SakuraEngine/vcpkg-registry", baseline = "e0e1e83ec66e3c9b36066f79d133b01eb68049f7", packages = { "skrgamenetworkingsockets" } } } add_requires("vcpkg::skrgamenetworkingsockets >=1.4.0+1", {configs = {registries = registries}}) ``` ## Using Conan dependency package ```lua add_requires("conan::zlib/1.2.11", {alias = "zlib", debug = true}) add_requires("conan::openssl/1.1.1g", {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: ```sh 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 (debug) -> conan::openssl/1.1.1g please input: y (y/n) => installing conan::zlib/1.2.11 .. ok => installing conan::openssl/1.1.1g .. ok [ 0%]: cache compiling.release src/main.c [100%]: linking.release test ``` Custom Conan/settings: ```lua add_requires("conan::poco/1.10.0", {alias = "poco", configs = {settings = {"compiler=gcc", "compiler.libcxx=libstdc++11"}}}) ``` Some other Conan related configuration items: ``` { build = {description = "use it to choose if you want to build from sources.", default = "missing", values = {"all", "never", "missing", "outdated"}}, remote = {description = "Set the conan remote server."}, options = {description = "Set the options values, e.g. OpenSSL:shared=True"}, imports = {description = "Set the imports for conan."}, settings = {description = "Set the build settings for conan."}, build_requires = {description = "Set the build requires for conan.", default = "xmake_generator/0.1.0@bincrafters/testing"} } ``` ## Using Conda dependency package ```lua add_requires("conda::zlib 1.2.11", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` ## Using Pacman dependency package We support not only the installation and integration of the pacman package on archlinux, but also the installation and integration of the mingw x86\_64/i386 package of pacman on msys2. ```lua add_requires("pacman::zlib", {alias = "zlib"}) add_requires("pacman::libpng", {alias = "libpng"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libpng") ``` On Arch Linux: ```sh xmake ``` To install the mingw package on msys2, you need to specify the mingw platform: ```sh xmake f -p mingw -a [x86_64|i386] xmake ``` ## Using 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("test") set_kind("binary") add_files("clib/bytes/*.c") add_files("src/*.c") add_packages("bytes") ``` ## Using Dub/Dlang dependency package xmake also supports Dlang's dub package manager and integrates Dlang's dependent packages to use. ```lua add_rules("mode.debug", "mode.release") add_requires("dub::log 0.4.3", {alias = "log"}) add_requires("dub::dateparser", {alias = "dateparser"}) add_requires("dub::emsi_containers", {alias = "emsi_containers"}) add_requires("dub::stdx-allocator", {alias = "stdx-allocator"}) add_requires("dub::mir-core", {alias = "mir-core"}) target("test") set_kind("binary") add_files("src/*.d") add_packages("log", "dateparser", "emsi_containers", "stdx-allocator", "mir-core") ``` ## Using dependency package of ubuntu/apt After v2.5.4 support the use of apt to integrate dependent packages, and will also automatically find packages that have been installed on the ubuntu system. ```lua add_requires("apt::zlib1g-dev", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` ## Using gentoo/portage dependency package After v2.5.4 support the use of Portage integrated dependency packages, and will also automatically find packages already installed on the Gentoo system. ```lua add_requires("portage::libhandy", {alias = "libhandy"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("libhandy") ``` ## Using nimble's dependency package After v2.5.8, it supports the integration of packages in the nimble package manager, but it is currently only used for nim projects, because it does not provide binary packages, but directly installed nim code packages. ```lua add_requires("nimble::zip >1.3") target("test") set_kind("binary") add_files("src/main.nim") add_packages("nimble::zip") ``` ## Using cargo's dependency package Cargo dependency packages are mainly used for rust project integration, for example: ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` However, we can also use cxxbridge in C++ to call the Rust library interface to reuse all Rust packages in disguise. For a complete example, see: [Call Rust in C++](https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cxx_call_rust_library) ## Using NuGet dependency packages After 2.9.7, we also support getting native libraries from dotnet/nuget and quickly integrating them. ```lua add_requires("nuget::zlib_static", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib") ``` --- --- url: /api/scripts/extension-modules/utils/archive.md --- # utils.archive This module is used to compress and decompress files. It supports decompression of most common compression formats. It will automatically detect which compression tools are provided by the system, and then will use the most appropriate compression tool for the operation. ## archive.archive * zip files #### Function Prototype ::: tip API ```lua archive.archive(archivefile: , outputdir: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | archivefile | Required. Archive file path | | outputdir | Required. Output directory path | | options | Optional. Configuration options, supports the following:- `curdir` - Current working directory- `recurse` - Whether to recurse directories- `compress` - Compression quality (fastest|faster|default|better|best)- `excludes` - Exclude files list | #### Return Value No return value #### Usage ```lua import("utils.archive") archive.archive("/tmp/a.zip", "/tmp/outputdir") archive.archive("/tmp/a.7z", "/tmp/outputdir") archive.archive("/tmp/a.gzip", "/tmp/outputdir") archive.archive("/tmp/a.tar.bz2", "/tmp/outputdir") ``` Some configuration options can also be added, such as recursive directories, compression quality, exclude files, etc. ```lua import("utils.archive") local options = {} options.curdir = "/tmp" options.recurse = true options.compress = "fastest|faster|default|better|best" options.excludes = {"*/dir/*", "dir/*"} archive.archive("/tmp/a.zip", "/tmp/outputdir", options) ``` ## archive.extract * unzip files #### Function Prototype ::: tip API ```lua archive.extract(archivefile: , outputdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | archivefile | Required. Archive file path | | outputdir | Required. Output directory path | #### Return Value No return value #### Usage ```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") ``` --- --- url: /zh/api/scripts/extension-modules/utils/archive.md --- # utils.archive 此模块用于压缩和解压文件。支持大部分常用压缩格式的解压缩,它会自动检测系统提供了哪些压缩工具,然后会使用最合适的压缩工具进行操作。 ## archive.archive * 压缩文件 #### 函数原型 ::: tip API ```lua archive.archive(archivefile: , outputdir: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | archivefile | 必需。压缩文件路径 | | outputdir | 必需。输出目录路径 | | options | 可选。配置选项,支持以下选项:- `curdir` - 当前工作目录- `recurse` - 是否递归目录- `compress` - 压缩质量(fastest|faster|default|better|best)- `excludes` - 排除文件列表 | #### 返回值说明 无返回值 #### 用法说明 ```lua import("utils.archive") archive.archive("/tmp/a.zip", "/tmp/outputdir") archive.archive("/tmp/a.7z", "/tmp/outputdir") archive.archive("/tmp/a.gzip", "/tmp/outputdir") archive.archive("/tmp/a.tar.bz2", "/tmp/outputdir") ``` 还可以添加一些配置选项,如递归目录,压缩质量,排除文件等。 ```lua import("utils.archive") local options = {} options.curdir = "/tmp" options.recurse = true options.compress = "fastest|faster|default|better|best" options.excludes = {"*/dir/*", "dir/*"} archive.archive("/tmp/a.zip", "/tmp/outputdir", options) ``` ## archive.extract * 解压文件 #### 函数原型 ::: tip API ```lua archive.extract(archivefile: , outputdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | archivefile | 必需。压缩文件路径 | | outputdir | 必需。输出目录路径 | #### 返回值说明 无返回值 #### 用法说明 ```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") ``` --- --- url: /api/scripts/extension-modules/utils/platform.md --- # utils.platform This module is used for some platform-related auxiliary operation interfaces ## utils.platform.gnu2mslib * Convert mingw's libxxxdll.a to msvc's xxx.lib library #### Function Prototype ::: tip API ```lua gnu2mslib(outputlib: , inputfile: , options:
) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | outputlib | Required. Output .lib file path | | inputfile | Required. Input .dll.a or .def file path | | options | Optional. Option parameters, supports the following:- `dllname` - DLL file name- `arch` - Architecture (e.g. "x64") | #### Return Value No return value #### Usage This feature is particularly helpful for Fortran & C++ mixed projects, because VS does not provide the fortran compiler. You can only use MinGW's gfortran to compile the fortran part, and then link with the VS project. Often such projects also have some other libraries provided in the vs format, so pure MinGW compilation is not possible, you can only use this function to mix compilation. And cmake also has a similar [GNUtoMS](https://cmake.org/cmake/help/latest/prop_tgt/GNUtoMS.html). For related issues, see: [#1181](https://github.com/xmake-io/xmake/issues/1181) ```lua import("utils.platform.gnu2mslib") gnu2mslib("xxx.lib", "xxx.dll.a") gnu2mslib("xxx.lib", "xxx.def") gnu2mslib("xxx.lib", "xxx.dll.a", {dllname = "xxx.dll", arch = "x64"}) ``` Support to generate xxx.lib from def, and also support to automatically export .def from xxx.dll.a, and then generate xxx.lib If you don’t want to automatically generate def from dll.a, but want to borrow the def generated by gnu linker, then configure it yourself through add\_shflags("-Wl,--output-def,xxx.def") to generate def, and then pass in def to this interface. . `{dllname = xxx, arch = "xxx"}` These are optional, according to your needs. You can also directly xmake l utils.platform.gnu2mslib xxx.lib xxx.dll.a quick test verification --- --- url: /zh/api/scripts/extension-modules/utils/platform.md --- # utils.platform 此模块用于一些平台相关的辅助操作接口 ## utils.platform.gnu2mslib * 将 mingw 的 libxxxdll.a 转换成 msvc 的 xxx.lib 库 #### 函数原型 ::: tip API ```lua gnu2mslib(outputlib: , inputfile: , options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | outputlib | 必需。输出的 .lib 文件路径 | | inputfile | 必需。输入的 .dll.a 或 .def 文件路径 | | options | 可选。选项参数,支持以下选项:- `dllname` - DLL 文件名- `arch` - 架构(如 "x64") | #### 返回值说明 无返回值 #### 用法说明 这个功能对 Fortran & C++ 混合项目特别有帮助,因为 VS 不提供fortran编译器,只能用MinGW的gfortran来编译fortran部分,然后和VS的项目链接。 往往这样的项目同时有一些其他的库以vs格式提供,因此纯用MinGW编译也不行,只能使用这个功能来混合编译。 而 cmake 也有个类似的 [GNUtoMS](https://cmake.org/cmake/help/latest/prop_tgt/GNUtoMS.html)。 相关 issues 见:[#1181](https://github.com/xmake-io/xmake/issues/1181) ```lua import("utils.platform.gnu2mslib") gnu2mslib("xxx.lib", "xxx.dll.a") gnu2mslib("xxx.lib", "xxx.def") gnu2mslib("xxx.lib", "xxx.dll.a", {dllname = "xxx.dll", arch = "x64"}) ``` 支持从 def 生成 xxx.lib ,也支持从 xxx.dll.a 自动导出 .def ,然后再生成 xxx.lib 如果不想自动从dll.a生成 def,想借用 gnu linker 生成的 def,那就自己通过 add\_shflags("-Wl,--output-def,xxx.def") 配置,生成 def,然后传入 def 到这个接口。。 `{dllname = xxx, arch = "xxx"}` 这些是可选的,根据自己的需求而定。。 也可以直接 xmake l utils.platform.gnu2mslib xxx.lib xxx.dll.a 快速测试验证 --- --- url: /api/scripts/builtin-modules/vformat.md --- # vformat * Formatting strings, support for built-in variable escaping #### Function Prototype ::: tip API ```lua vformat(formatstring: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | formatstring | Format string | | ... | Variable arguments for formatting | #### Usage This interface is followed by [format](/api/scripts/builtin-modules/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) ``` --- --- url: /zh/api/scripts/builtin-modules/vformat.md --- # vformat * 格式化字符串,支持内置变量转义 #### 函数原型 ::: tip API ```lua vformat(formatstring: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | formatstring | 格式字符串 | | ... | 格式化的可变参数 | #### 用法说明 此接口跟[format](/api/scripts/builtin-modules/format)接口类似,只是增加对内置变量的获取和转义支持。 ```lua local s = vformat("hello %s $(mode) $(arch) $(env PATH)", xmake) ``` --- --- url: /api/scripts/builtin-modules/winos.md --- # winos The windows system operation module is a built-in module, no need to use [import](/api/scripts/builtin-modules/import) to import, you can directly call its interface in the script scope. ## winos.version * Get windows system version #### Function Prototype ::: tip API ```lua winos.version() ``` ::: #### Parameter Description No parameters required for this function. #### Usage The version returned is the semver semantic version object ```lua if winos.version():ge("win7") then - ... end if winos.version():ge("6.1") then - ... end ``` In addition, it can also support the direct judgment of the windows version name. The mapping rules are as follows: ``` nt4 = "4.0" win2k = "5.0" winxp = "5.1" ws03 = "5.2" win6 = "6.0" vista = "6.0" ws08 = "6.0" longhorn = "6.0" win7 = "6.1" win8 = "6.2" winblue = "6.3" win81 = "6.3" win10 = "10.0" ``` ## winos.registry\_keys * Get the list of registry builds #### Function Prototype ::: tip API ```lua winos.registry_keys(pattern: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | pattern | Pattern string for matching registry keys | #### Usage Support through pattern matching, traverse to obtain the registry key path list, `*` is single-level path matching, `**` is recursive path matching. ```lua local keypaths = winos.registry_keys("HKEY_LOCAL_MACHINE\\SOFTWARE\\*\\Windows NT\\*\\CurrentVersion\\AeDebug") for _, keypath in ipairs(keypaths) do print(winos.registry_query(keypath .. ";Debugger")) end ``` ## winos.registry\_values * Get a list of registry value names #### Function Prototype ::: tip API ```lua winos.registry_values(pattern: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | pattern | Pattern string for matching registry values | #### Usage Support to obtain the value name list of the specified key path through pattern matching, and the string after the `;` is the specified key name pattern matching string. ```lua local valuepaths = winos.registry_values("HKEY_LOCAL_MACHINE\\SOFTWARE\\xx\\AeDebug;Debug*") for _, valuepath in ipairs(valuepaths) do print(winos.registry_query(valuepath)) end ``` ## winos.registry\_query * Get the registry value #### Function Prototype ::: tip API ```lua winos.registry_query(keypath: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | keypath | Registry key path | #### Usage Get the value under the specified registry path, if the value name is not specified, then get the default value of the key path ```lua local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug") local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger") ``` --- --- url: /zh/api/scripts/builtin-modules/winos.md --- # winos Windows 系统操作模块,属于内置模块,无需使用[import](/zh/api/scripts/builtin-modules/import)导入,可直接脚本域调用其接口。 ## winos.version * 获取 windows 系统版本 #### 函数原型 ::: tip API ```lua winos.version() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 返回的版本是 semver 语义版本对象 ```lua if winos.version():ge("win7") then -- ... end if winos.version():ge("6.1") then -- ... end ``` 并且,还可以支持对 windows 版本名的直接判断,映射规则如下: ``` nt4 = "4.0" win2k = "5.0" winxp = "5.1" ws03 = "5.2" win6 = "6.0" vista = "6.0" ws08 = "6.0" longhorn = "6.0" win7 = "6.1" win8 = "6.2" winblue = "6.3" win81 = "6.3" win10 = "10.0" ``` ## winos.registry\_keys * 获取注册表建列表 #### 函数原型 ::: tip API ```lua winos.registry_keys(pattern: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | pattern | 用于匹配注册表键的模式字符串 | #### 用法说明 支持通过模式匹配的方式,遍历获取注册表键路径列表,`*` 为单级路径匹配,`**` 为递归路径匹配。 ```lua local keypaths = winos.registry_keys("HKEY_LOCAL_MACHINE\\SOFTWARE\\*\\Windows NT\\*\\CurrentVersion\\AeDebug") for _, keypath in ipairs(keypaths) do print(winos.registry_query(keypath .. ";Debugger")) end ``` ## winos.registry\_values * 获取注册表值名列表 #### 函数原型 ::: tip API ```lua winos.registry_values(pattern: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | pattern | 用于匹配注册表值的模式字符串 | #### 用法说明 支持通过模式匹配的方式,获取指定键路径的值名列表,`;` 之后的就是指定的键名模式匹配字符串。 ```lua local valuepaths = winos.registry_values("HKEY_LOCAL_MACHINE\\SOFTWARE\\xx\\AeDebug;Debug*") for _, valuepath in ipairs(valuepaths) do print(winos.registry_query(valuepath)) end ``` ## winos.registry\_query * 获取注册表建值 #### 函数原型 ::: tip API ```lua winos.registry_query(keypath: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | keypath | 注册表键路径 | #### 用法说明 获取指定注册表建路径下的值,如果没有指定值名,那么获取键路径默认值 ```lua local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug") local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger") ``` --- --- url: /zh/posts/xmake-in-2020.md --- title: Xmake 2020 年终总结 tags: \[xmake, lua, C/C++] date: 2020-12-30 author: Ruki *** 2020 年,[xmake](https://github.com/xmake-io/xmake) 总共迭代发布了9个版本,新增了 1871 commits,1k+ stars,新增处理了 500+ 的 issues/pr。 ![](/assets/img/posts/xmake/star-history-2020.png) ### 简介 还不知道 xmake 是什么的同学,这里先做个简单的介绍: xmake 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。 虽然,简单易用是 xmake 的一大特色,但 xmake 的功能也是非常强大的,既能够像 Make/Ninja 那样可以直接编译项目,也可以像 CMake/Meson 那样生成工程文件,还有内置的包管理系统来帮助用户解决 C/C++依赖库的集成使用问题。 目前,xmake主要用于C/C++项目的构建,但是同时也支持其他native语言的构建,可以实现跟C/C++进行混合编译,同时编译速度也是非常的快,可以跟Ninja持平。 ### 日活破 100 5年了,终于破100了(好悲催~),但相比去年已经算是有了很大的增长,每天最多有 113 人,304 个工程在使用 xmake 进行构建。 ![](/assets/img/posts/xmake/build-stats-2020.png) ### 上线官方课程 今年还上线了一门 xmake 相关的入门课程:[Xmake 带你轻松构建 C/C++ 项目](https://xmake.io/zh/) ### 发布独立的 C/C++ 包管理器 (Xrepo) 今年,xmake 对包管理的集成使用做了很大的改进和完善,为了方便日常管理维护依赖包,我们新增了独立的 C/C++ 包管理工具 [Xrepo](https://github.com/xmake-io/xrepo) 可以快速安装 C/C++ 依赖包 ![](https://xrepo.xmake.io/assets/img/xrepo.gif) ### 上线 xrepo 包文档站 同时,我们还上线了 xrepo 包的 [文档站](https://xrepo.xmake.io/#/zh-cn/), 我们可以在上面快速检索每个包的使用和集成方式,以及查看当前支持的平台列表和安装方式。 ### 官方 xmake-repo 仓库新增 200+ 常用包 非常感谢各位 xmake 的贡献者,使得 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库在今年新增了 200+ 的常用 C/C++ 包,我们可以很方便的在项目中快速集成使用它们。 虽然里面的包还是很少,但是没有关系,xmake 也支持直接集成 vcpkg/conan/clib/homebrew/dub/pacman 等其他的管理仓库中的 C/C++ 包。 ``` add_requires("tbox >1.6.1", "libuv master") add_requires("vcpkg::ffmpeg", {alias = "ffmpeg"}) add_requires("brew::pcre2/libpcre2-8", {alias = "pcre2"}) add_requires("conan::openssl/1.1.1g", {alias = "openssl"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("tbox", "libuv", "ffmpeg", "pcre2", "openssl") ``` ### 今年 xmake 的一些新增特性 今年,我们也对 xmake 做了很多的迭代改进,新增了不少实用的新特性,例如: * 改进依赖包集成体验,并新增依赖包的 license 自动检测机制 * 增加对 Intel 编译工具链的支持 * 增加对 Zig,Fortran 等其他语言项目的编译支持 * 增加对 Wasm 工具链以及 Qt for wasm 项目的编译支持 * 重构了整个工具链模块,实现更加方便的自定义工具链,更加快速灵活的工具链切换 * 新增 iOS/MacOS App 应用程序,Frameworks 和 bundle 程序的编译支持,可以完全脱离 xcodebuild,也能从源码编译生成 ipa 包 * 对 xmake 的整体构建速度进行了大提速,编译速度完全媲美 ninja * 新增了 build.ninja 工程文件生成,并对现有 vs/vsxmake 工程进行了很多的改进 * 新增 try-build 构建模式,可以使用 xmake 直接编译第三方构建系统的项目,例如直接编译 cmake, ninja, gn, autotools, android.mk 等维护的项目,同时还能支持交叉编译环境的自动配置 * 重构支持了 socket/pipe/process + 协程 进行统一调度,为下一步跨平台分布式编译做准备 其他细节特性和改进还有很多,我就不一一列举了。 ### Xmake Discord 社区频道上线 Discord (discord.com) 是一个非常不错的全端即时交流平台,为了更好地和国内外用户进行即时交流,我们在上面创建了中文和英文两个不同的频道,界面大概长这样。 ![](https://tboox.org/static/img/xmake/discord.png) 对 xmake 感兴趣的朋友可以点击 [服务器邀请链接](https://discord.gg/XXRp26A4Gr) 加入我们的频道,当然话题仅限于 xmake 相关项目。 ### 新一年的目标 * 改进和完善现有C/C++包管理集成 * xmake-repo 仓库新增收录1000+ 的常用包 * 实现跨平台的分布式编译 * 重写 xmake-idea 插件 最后,再晒张我个人今年在开源上的总体贡献图,明年继续~ 有对 xmake 感兴趣的朋友,欢迎关注我们,请戳这: ![](/assets/img/posts/xmake/commits-2020.png) --- --- url: /posts/api-scope.md --- Although xmake's project description file `xmake.lua` is based on lua syntax, xmake has wrapped it with an additional layer to make writing project build logic more convenient and concise, so that writing `xmake.lua` won't be as tedious as writing makefiles. Basically, writing a simple project build description only takes three lines, for example: ```lua target("test") set_kind("binary") add_files("src/*.c") ``` Then just compile and run it: ```bash $ xmake run test ``` This greatly improves development efficiency for those who want to write some test code temporarily. ### Scope and Project Description Syntax xmake's description syntax is divided by scope, mainly divided into: * External scope * Internal scope So which ones belong to external scope and which ones belong to internal scope? Looking at the comments below, you'll get a general idea: ```lua -- External scope target("test") -- External scope set_kind("binary") add_files("src/*.c") on_run(function () -- Internal scope end) after_package(function () -- Internal scope end) -- External scope task("hello") -- External scope on_run(function () -- Internal scope end) ``` Simply put, everything inside custom scripts `function () end` belongs to internal scope, which is the script scope. Everything else belongs to external scope. #### External Scope For most projects, you don't need very complex project descriptions or custom script support. Simple `set_xxx` or `add_xxx` can meet the requirements. According to the Pareto principle, 80% of the time, we only need to write like this: ```lua target("test") set_kind("static") add_files("src/test/*.c") target("demo") add_deps("test") set_kind("binary") add_links("test") add_files("src/demo/*.c") ``` No complex API calls, no various tedious variable definitions, and no `if` judgments and `for` loops. What we want is simplicity and readability. At a glance, even if you don't understand lua syntax, it doesn't matter. Just treat it as simple description syntax, which looks a bit like function calls. Anyone with basic programming knowledge can basically see how to configure it at a glance. To achieve simplicity and security, in this scope, many lua built-in APIs are not exposed, especially those related to writing files and modifying the operating environment. Only some basic read-only interfaces and logical operations are provided. Currently, the lua built-in APIs exposed in external scope are: * `table` * `string` * `pairs` * `ipairs` * `print`: Modified version, providing formatted printing support * `os`: Only provides read-only interfaces, such as `getenv`, etc. Of course, although not many built-in lua APIs are provided, xmake also provides many extension APIs. The description APIs are not mentioned in detail. For details, please refer to: [Project Description API Documentation](https://github.com/waruqi/xmake/wiki/%E5%B7%A5%E7%A8%8B%E6%8F%8F%E8%BF%B0api%E6%96%87%E6%A1%A3) There are also some auxiliary APIs, such as: * `dirs`: Scan and get all directories in the specified path * `files`: Scan and get all files in the specified path * `format`: Format string, shorthand version of `string.format` Variable definitions and logical operations can also be used. After all, it's based on lua. The basic syntax should still be available. We can use `if` to switch compilation files: ```lua target("test") set_kind("static") if is_plat("iphoneos") then add_files("src/test/ios/*.c") else add_files("src/test/*.c") end ``` We can also enable and disable a certain sub-project target: ```lua if is_arch("arm*") then target("test1") set_kind("static") add_files("src/*.c") else target("test2") set_kind("static") add_files("src/*.c") end ``` Note that variable definitions are divided into global variables and local variables. Local variables are only valid for the current `xmake.lua` and do not affect sub `xmake.lua` files: ```lua -- Local variable, only valid for current xmake.lua local var1 = 0 -- Global variable, affects all subsequent sub xmake.lua files included by add_subfiles(), add_subdirs() var2 = 1 add_subdirs("src") ``` #### Internal Scope Also known as plugin and script scope, it provides more complex and flexible script support, generally used for writing custom scripts, plugin development, custom task tasks, custom modules, etc. Generally, everything contained by `function () end` and passed into `on_xxx`, `before_xxx` and `after_xxx` interfaces belongs to internal scope. For example: ```lua -- Custom script target("hello") after_build(function () -- Internal scope end) -- Custom task, plugin task("hello") on_run(function () -- Internal scope end) ``` In this scope, you can not only use most of lua's APIs, but also use many extension modules provided by xmake. All extension modules are imported through `import`. For details, please refer to: [Plugin Development: Import Libraries](https://xmake.io/) Here we give a simple example. After compilation, sign the ios target program with ldid: ```lua target("iosdemo") set_kind("binary") add_files("*.m") after_build( function (target) -- Execute signing, if it fails, automatically interrupt and give highlighted error information os.run("ldid -S$(projectdir)/entitlements.plist %s", target:targetfile()) end) ``` Note that in internal scope, all calls enable exception capture mechanism. If an error occurs during operation, xmake will automatically interrupt and give error prompt information. Therefore, scripts don't need tedious `if retval then` judgments, and script logic is more clear at a glance. #### Interface Scope All description API settings in external scope also have scope distinctions. Calling them in different places has different impact ranges, for example: ```lua -- Global root scope, affects all targets, including sub-project target settings in add_subdirs() add_defines("DEBUG") -- Define or enter demo target scope (supports multiple entries to append settings) target("demo") set_kind("shared") add_files("src/*.c") -- Current target scope, only affects current target add_defines("DEBUG2") -- Option settings, only support local settings, not affected by global API settings option("test") -- Current option's local scope set_default(false) -- Other target settings, -DDEBUG will also be set target("demo2") set_kind("binary") add_files("src/*.c") -- Re-enter demo target scope target("demo") -- Append macro definition, only valid for current demo target add_defines("DEBUG3") ``` xmake also has some global APIs that only provide global scope support, for example: * `add_subfiles()` * `add_subdirs()` * `add_packagedirs()` etc. These calls should not be placed between the local scopes of `target` or `option`. Although there is no actual difference, it will affect readability and can easily be misleading. Usage is as follows: ```lua target("xxxx") set_kind("binary") add_files("*.c") -- Include sub-module files add_subdirs("src") ``` #### Scope Indentation Indentation in `xmake.lua` is just a writing specification, used to more clearly distinguish which scope the current setting is for. Although it's okay even without indentation, readability is not very good. For example: ```lua target("xxxx") set_kind("binary") add_files("*.c") ``` and ```lua target("xxxx") set_kind("binary") add_files("*.c") ``` The above two methods have the same effect, but in terms of understanding, the first one is more intuitive. At a glance, you can see that `add_files` is only set for the target, not a global setting. Therefore, appropriate indentation helps to better maintain `xmake.lua`. Finally, here are [tbox](https://github.com/waruqi/tbox)'s [xmake.lua](https://github.com/waruqi/tbox/blob/master/xmake.lua) and [src/tbox/xmake.lua](https://github.com/waruqi/tbox/blob/master/src/tbox/xmake.lua) descriptions for reference. --- --- url: /zh/posts/xmake-community.md --- [xmake](https://github.com/xmake-io/xmake) 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。 我们可以用 xmake 很方便的开发构建 C/C++ 项目,同时也支持和其他 native 语言的混合编译。 ![](/assets/img/index/xmake-basic-render.gif) 近期,我们还新增了一个基于 xmake 的独立子命令:[xrepo](https://github.com/xmake-io/xrepo/),一个完整独立的跨平台 C/C++ 包管理器,便于用户更加方便的管理日常 C/C++ 包的安装和集成使用。 ![](https://xrepo.xmake.io/assets/img/xrepo.gif) 关于 xmake 和 xrepo 的进一步了解和使用可以参考下面的链接。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) * [入门课程](https://xmake.io/zh/) * [Xrepo 命令](https://github.com/xmake-io/xrepo) ### Discord 社区频道上线 Discord (discord.com) 是一个非常不错的全端即时交流平台,我们在上面创建了中文和英文两个不同的频道,界面大概长这样。 ![](https://tboox.org/static/img/xmake/discord.png) 最近,我们在 Discord 上新创建了 xmake 技术交流频道,欢迎大家加入。 对 xmake 感兴趣的朋友可以点击 [服务器邀请链接](https://discord.gg/XXRp26A4Gr) 加入我们的频道,当然话题仅限于 xmake 相关项目。 另外,在 xmake 支持 Zig Language 项目编译的过程中,我们也有幸邀请到了 Zig 作者 Andrewrk 进入我们的社区频道。 ![](https://tboox.org/static/img/xmake/zig_author.png) ### 其他平台社区 另外,xmake 还有在其他平台的创建交流社区,大家可以使用平常最常用的交流平台进行技术交流。 * Reddit论坛: https://www.reddit.com/r/xmake * Telegram群组: https://t.me/tbooxorg * Gitter聊天室: https://gitter.im/xmake-io/xmake * QQ群:662147501 ### 微信公众号 也欢迎关注我们的微信公众号:tboox-os,不定期获取 xmake 的相关资讯和技术文章。 --- --- url: /posts/quickstart-1-installation.md --- Xmake is a lightweight modern C/C++ project build tool based on Lua. Its main features are: easy to use syntax, more readable project maintenance, and a consistent build experience across platforms. This article mainly explains the installation process of xmake under various platforms. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ## Install the Master version Usually we only need to install the script with a one-click installation script. ### Using curl ```bash Bash <(curl -fsSL https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh) ``` ### Using wget ```bash Bash <(wget https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh -O -) ``` ### Using powershell ```bash Invoke-Expression (Invoke-Webrequest 'https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.ps1' -UseBasicParsing).Content ``` Note: If the ps script execution prompt fails, you can try to execute in administrator mode. ## Install Windows version ### Using the installation package Windows provides a pre-made nsis installation package, we can download the installation package directly from github's Releases download page. 1. Download the windows installation package from \[Releases] (https://github.com/xmake-io/xmake/releases) 2. Run the installer xmake-\[version].exe ### Using scoop ```bash Scoop install xmake ``` ## MacOS ```bash $ ruby ​​-e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" $ brew install xmake ``` or: 1. Download the pkg installation package from \[Releases] (https://github.com/xmake-io/xmake/releases) 2. Double click to run Or install the master version: ```bash # Install the master version using homebrew $ brew install xmake --HEAD # or directly call the shell to download and install $ bash <(curl -fsSL https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh) ``` ## Linux Install on archlinux: ```bash $ yaourt xmake ``` Or download the deb package to install: 1. Download the deb installation package from \[Releases] (https://github.com/xmake-io/xmake/releases) 2. Run: `dpkg -i xmake-xxxx.deb` ## Termux The latest version of xmake already supports termux well, and we usually only need to execute the above one-click installation script. If it fails, please refer to the following to pull the source code to compile and install. ## Source compilation and installation ### Installation Note: Remember, xmake is not recommended to install under root, so try not to pull down the source to compile and install! ```bash $ git clone --recursive https://github.com/xmake-io/xmake.git $ cd ./xmake $ ./scripts/get.sh __local__ $ source ~/.xmake/profile ``` If you think the source of github is too slow, you can pull it through gitee's image source: `clone --recursive https://gitee.com/tboox/xmake.git` Note: Since the current xmake source maintains dependencies via git submodule, you need to add the `--recursive` parameter to pull all submodules at the same time. Please do not download the tar.gz source directly, because github does not automatically package submodules. Code. If you forget to add `--recursive` when git clone, you can also execute `git submodule update --init` to pull all submodules, for example: ```bash $ git clone https://github.com/xmake-io/xmake.git $ cd ./xmake $ git submodule update --init $ ./scripts/get.sh __local__ ``` Note: `./get.sh __local__` is installed to `~/.local/xmake`, and then loaded by `source ~/.xmake/profile`, so after the installation, the current terminal fails to execute xmake, If the prompt is not found, manually execute `source ~/.xmake/profile`, and the next time you open the terminal, you don't need it. ### Uninstall ```bash $ ./scripts/get.sh __uninstall__ ``` ### Just update the installation lua script This developer needs to debug the xmake source locally: ```bash $ ./scripts/get.sh __local__ __install_only__ ``` ### Root installation Xmake is not recommended for root installation, because this is very insecure. If the user has to download the root, if the prompt xmake fails to run, please pass the `--root` parameter as prompted or set `XMAKE_ROOT=y`. The environment variable is forcibly enabled, provided that the user needs to pay attention to the risk of incorrect operating system file files under root. ### Dependency issues 1. If you encounter problems with readline, please install readline-devel or libreadline-dev dependencies. This is optional. It is only needed when the `xmake lua` command executes REPL. 2. If you want to speed up compilation, you can install ccache, xmake will automatically detect and use, which is also optional. ## Other installation methods Note: This is also the source code compilation and installation, but the installation path will be directly written to `/usr/`, which requires root privileges, so unless special circumstances, this installation method is not recommended, it is recommended to use the `./get. Sh __local__` way to install, the installation path of the two installation methods is different, do not mix. Compile and install via make: ```bash $ make build; sudo make install ``` Install to other specified directories: ```bash $ sudo make install prefix=/usr/local ``` Uninstall: ```bash $ sudo make uninstall ``` ## Update Upgrade Starting with v2.2.3, the `xmake update` command has been added to quickly update and upgrade itself. The default is to upgrade to the latest version. Of course, you can also specify to upgrade or roll back to a version: ```bash $ xmake update 2.2.4 ``` We can also specify an update to the master/dev branch version: ```bash $ xmake update master $ xmake update dev ``` Update from the specified git source ```bash $ xmake update github:xmake-io/xmake#master $ xmake update gitee:tboox/xmake#dev # gitee mirror ``` If xmake/core hasn't moved, just update the xaake lua script changes, you can add `-s/--scriptonly` to quickly update the lua script. ```bash $ xmake update -s dev ``` Finally, if we want to uninstall xmake, it is also supported: `xmake update --uninstall` --- --- url: /posts/quickstart-10-target-deps.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. This article mainly explains in detail how to maintain and generate multiple target files in a project, and how to set dependencies between them. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### What is target? In xmake's concept definition, an independent project may have multiple sub-projects organized together. Each sub-project corresponds to only one unique target file that can be generated, such as: executable programs, static libraries, or dynamic libraries, etc. Each sub-project mentioned here is what xmake calls a `target`, literally meaning `target sub-project`. Therefore, for each sub-project, we can maintain it in xmake.lua by adding a new target, for example: ```lua target("test1") set_kind("binary") add_files("src/test1/*.c") target("test2") set_kind("binary") add_files("src/test2/*.c") ``` Above we have defined two independent sub-project targets, which will generate two independent executable files when compiling. ### Inheriting Global Settings from Root Scope Let's not talk about dependencies between targets for now. If we have many common settings, and each target has to set them, it would be very redundant and difficult to maintain. Therefore, we can move these configurations outside the target scope, that is, set them in the root scope, so that they will take effect for all targets in the current xmake.lua and all sub-xmake.lua files, for example: ```lua add_links("tbox") add_linkdirs("lib") add_includedirs("include") target("test1") set_kind("binary") add_files("src/test1/*.c") target("test2") set_kind("binary") add_files("src/test2/*.c") ``` For example, if both targets need to link the tbox library, placing it in the outer root scope setting, both test1 and test2 can add the corresponding links. ### Dependency Settings Between Targets So if a target needs to use a static library generated by another target, how should it be configured? One way is to manually specify the directory where the library generated by the corresponding target is located through `add_linkdirs` and `add_links`, and then add the link. ```lua target("foo") set_kind("static") add_files("foo/*.c") add_defines("FOO") target("test1") set_kind("binary") add_includedirs("foo/inc") add_links("foo") add_linkdirs("$(buildir)") add_files("test1/*.c") add_defines("FOO") target("test2") set_kind("binary") add_includedirs("foo/inc") add_links("foo") add_linkdirs("$(buildir)") add_files("test2/*.c") add_defines("FOO") ``` In the above configuration, both test1 and test2 will use the libfoo library, and need to obtain the header file path, library path, and link of the libfoo library. In addition, they need to set the `-DFOO` macro definition switch during use. It doesn't look like much, but there are actually two problems with writing it this way: 1. There is a compilation order dependency between the test targets and the other two library targets. If test compiles first, it will prompt that the link library cannot be found. 2. The configuration is too cumbersome and difficult to maintain. test1 and test2 have a lot of redundant configuration. Is there a simpler and more reliable configuration method? Actually, we just need to use `add_deps` to configure dependencies between targets. ```lua target("foo") set_kind("static") add_files("*.c") add_defines("FOO", {public = true}) add_includedirs("foo/inc", {public = true}) target("test1") set_kind("binary") add_deps("foo") add_files("test1/*.c") target("test2") set_kind("binary") add_deps("foo") add_files("test2/*.c") ``` Comparing the configurations of test1 and test2, isn't it much more concise? Just through `add_deps("foo")`, it inherits all the exported settings of libfoo: linkdirs, links, includedirs, and defines. The library generated by the target itself will automatically export link settings by default, and by setting the public attribute for includedirs and defines, we also mark them as exported, so they can be inherited by the test target. And now, with the dependency relationship, xmake will automatically handle the compilation order between these targets during compilation, ensuring that the libfoo library has been generated when linking. ### Further Analysis of Dependency Inheritance #### Cascading Dependency Inheritance According to what was said above, targets will automatically inherit configurations and properties from dependent targets, and there is no need to call interfaces like `add_links`, `add_linkdirs`, and `add_rpathdirs` to associate dependent targets. And the inheritance relationship supports cascading, for example: ```lua target("library1") set_kind("static") add_files("*.c") add_includedirs("inc") -- 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") ``` In the above configuration, test depends on library2, and library2 depends on library1. Then by adding only the library2 dependency through `add_deps`, test can completely inherit all exported settings on the entire dependency chain. #### Disabling Default Inheritance Behavior What if we don't want to inherit any configuration from dependent targets? How do we do it? ```lua add_deps("dep1", "dep2", {inherit = false}) ``` By explicitly setting the inherit configuration, we tell xmake whether the configurations of these two dependencies need to be inherited. If not set, inheritance is enabled by default. #### Detailed Explanation of Inheritable Export Properties Above, we also set public to true through `add_includedirs("inc1", {public = true})`, making the includedirs setting public for other dependent sub-targets to inherit. Currently, for target compilation and link flags related interface settings, inheritance properties are supported, and you can manually control whether they need to be exported to other targets for dependency inheritance. The currently supported properties are: | Property | Description | | ---- | ---- | | private | Default setting, as the current target's private configuration, will not be inherited by other dependent targets | | public | Public configuration, both the current target and dependent sub-targets will be set | | interface | Interface setting, only dependent sub-targets will inherit the setting, the current target does not participate | This actually references cmake's design. Currently in xmake, all compilation and link setting interfaces related to targets support visibility export, such as: `add_includedirs`, `add_defines`, `add_cflags`, etc. For more detailed information about this, please see: https://github.com/xmake-io/xmake/issues/368 --- --- url: /posts/quickstart-11-subprojects.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. This article mainly explains in detail how to organize and build a large-scale project by configuring sub-project modules. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### Maintaining Simple Project Structure For some lightweight small projects, usually only a single xmake.lua file is needed. The general structure is as follows: ``` projectdir - xmake.lua - src - test - *.c - demo - *.c ``` The source code hierarchy is simple. Usually, you only need to maintain one xmake.lua in the project root directory to define all targets to complete the build. It doesn't look very complicated and is very clear. ```lua -- Set common configuration in root scope, all current targets will take effect add_defines("COMMON") target("test") set_kind("static") add_files("src/test/*.c") add_defines("TEST") target("demo") set_kind("static") add_files("src/demo/*.c") add_defines("DEMO") ``` ### Maintaining Complex Project Structure But for some large projects, the organizational structure is usually very hierarchical and deep, and the number of target targets that need to be compiled may be dozens or even hundreds. At this time, if they are all maintained in the root xmake.lua file, it becomes a bit overwhelming. At this time, we need to create separate xmake.lua files in each sub-project module to maintain them, and then use the includes interface provided by xmake to include them according to the hierarchical relationship, eventually forming a tree structure: ``` projectdir - xmake.lua - src - test - xmake.lua - test1 - xmake.lua - test2 - xmake.lua - test3 - xmake.lua - demo - xmake.lua - demo1 - xmake.lua - demo2 - xmake.lua ... ``` Then, the root xmake.lua will include all sub-project xmake.lua files through hierarchical includes, so all target configurations defined in sub-projects will also be fully included. When compiling, we never need to switch to a sub-project directory to operate separately. We just need to: ```bash $ xmake build test1 $ xmake run test3 $ xmake install demo1 ``` This can compile, run, package, and install the specified sub-project targets. So unless there are special circumstances, it is not recommended to switch back and forth between directories to compile sub-projects separately, as it is very tedious. ### Root xmake.lua File Configuration The recommended approach is to configure only some common settings for all targets in the root xmake.lua, as well as includes references to sub-projects, without placing target definitions, for example: ```lua -- define project set_project("tbox") set_xmakever("2.3.2") set_version("1.6.5", {build = "%Y%m%d%H%M"}) -- set common flags set_warnings("all", "error") set_languages("c99") add_cxflags("-Wno-error=deprecated-declarations", "-fno-strict-aliasing", "-Wno-error=expansion-to-defined") add_mxflags("-Wno-error=deprecated-declarations", "-fno-strict-aliasing", "-Wno-error=expansion-to-defined") -- add build modes add_rules("mode.release", "mode.debug") -- includes sub-projects includes("test", "demo") ``` All settings in xmake are inherited in a tree-like manner. The root scope settings in the root xmake.lua will take effect for all targets in the included sub-xmake.lua files, but not the other way around. The root scope settings in sub-xmake.lua files only take effect for their sub-xmake.lua files below them, and will not affect the targets defined in the parent xmake.lua. ### Sub-xmake.lua File Configuration So, we can configure xmake.lua separately in each sub-project directory. All configurations inside will not interfere with the parent xmake.lua, and will only take effect for more granular sub-projects below it. This works layer by layer in a tree-like manner. Since most common configurations have already been configured in the root xmake.lua, we can focus on configuring settings that are only useful for test in the test sub-project, for example, for `projectdir/test/xmake.lua`: ```lua add_defines("TEST") target("test1") set_kind("static") add_files("test1/*.c") add_defines("TEST1") target("test2") set_kind("static") add_files("test2/*.c") add_defines("TEST2") ``` We can define all targets for test here. Of course, we can also continue to layer, maintaining xmake.lua separately in each test1, test2 directory. This depends on the scale of your project. For example: ```lua add_defines("TEST") includes("test1", "test2") ``` test1/xmake.lua ```lua target("test1") set_kind("static") add_files("test1/*.c") add_defines("TEST1") ``` test2/xmake.lua ```lua target("test2") set_kind("static") add_files("test2/*.c") add_defines("TEST2") ``` The `add_defines("TEST")` in the root scope here will take effect for both test1/test2 targets, but will not take effect for targets in the demo directory, because they are at the same level and have no tree-like inheritance relationship. ### Cross-xmake.lua Target Dependencies Although `projectdir/test/xmake.lua` and `projectdir/demo/xmake.lua` are two sub-project directories at the same level, and configurations cannot interfere with each other, targets can be accessed across xmake.lua files to achieve dependencies between targets. For example, if demo needs to depend on the test static library for linking, the xmake.lua under demo can be written like this: ```lua target("demo1") set_kind("binary") add_files("demo1/*.c") add_deps("test1") ``` As long as you associate the corresponding other sub-project target as a dependency through `add_deps("test1")`, the test1 static library will be compiled first, and the demo executable program will automatically link to the libtest1.a library it generates. ### File Path Hierarchy We need to remember that all path-related configuration interfaces, such as `add_files`, `add_includedirs`, etc., are relative to the directory where the current sub-project xmake.lua is located. So as long as the added files don't cross modules, you only need to consider the current relative path when setting them. ``` projectdir - test - xmake.lua - test1/*.c - test2/*.c ``` For example, the source file paths added here are all relative to the test sub-project directory. We don't need to set absolute paths, which simplifies things a lot. ```lua target("test1") add_files("test1/*.c") target("test2") add_files("test2/*.c") ``` Of course, if we have special needs and must set file paths under other sub-modules of the project? There are two methods: go out layer by layer through `../../`, or use the `$(projectdir)` built-in variable, which represents the global root directory of the project. For example, in the demo sub-project: ```lua target("demo1") set_kind("binary") add_files("demo1/*.c") add_files("../../test/test1/*.c") ``` Or: ```lua target("demo1") set_kind("binary") add_files("demo1/*.c") add_files("$(projectdir)/test/test1/*.c") ``` ### Advanced Usage of includes Interface #### Incorrect Usage The includes interface is a global interface and does not belong to any target, so please do not call it inside a target. The following is incorrect usage: ```lua target("test") set_kind("static") includes("test1", "test2") add_files("test/*.c") ``` The correct usage is: ```lua includes("test1", "test2") target("test") set_kind("static") add_files("test/*.c") ``` Or: ```lua target("test") set_kind("static") add_files("test/*.c") target_end() -- Call below, need to explicitly exit target scope first includes("test1", "test2") ``` #### Referencing Directories or Files In addition, includes can reference both directories and files directly. If an xmake.lua exists in the test1 directory, you can directly reference the directory with `includes("test1")`. If the test1 directory contains other project files named xxxx.lua, you can reference them by specifying the file: `includes("test1/xxxx.lua")`, with the same effect. #### Pattern Matching for Batch Import includes also supports batch importing multiple sub-projects through pattern matching, for example: ```lua includes("test/*/xmake.lua") ``` This can import configurations from all sub-project directories like test1, test2 under the test directory. If it's `**`, it also supports recursive multi-level matching: ```lua includes("test/**/xmake.lua") ``` Through pattern matching, we only need to do includes in one place in test/xmake.lua. In the future, when users add other sub-project xmake.lua files, they will be automatically imported, which is very convenient. #### Notes In addition, when using includes, one thing to note is that it is not C's `#include`. Therefore, when you includes sub-configurations in the current configuration, the current configuration will not be affected, for example: ```lua includes("xxx") target("test") -- ... ``` The above includes some sub-projects, but the configurations of these sub-projects will not interfere with the current test target program. --- --- url: /posts/quickstart-12-custom-scripts.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. This article mainly explains in detail how to achieve more complex and flexible customization in the script domain by adding custom scripts. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### Configuration Separation xmake.lua uses the 80/20 principle to achieve two-layer separated configuration: description domain and script domain. What is the 80/20 principle? Simply put, for most project configurations, 80% of the time, they are basic regular configurations, such as: `add_cxflags`, `add_links`, etc. Only the remaining less than 20% of places need additional complexity to meet some special configuration requirements. And this remaining 20% of configurations are usually more complex. If they are directly filled throughout xmake.lua, it will make the entire project configuration very messy and unreadable. Therefore, xmake uses two different configuration methods, description domain and script domain, to isolate 80% of simple configurations and 20% of complex configurations, making the entire xmake.lua look very clear and intuitive, with optimal readability and maintainability. ### Description Domain For novice users who are just getting started, or for maintaining some simple small projects, using only description configuration is already sufficient. So what is the description domain? It looks like this: ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") add_syslinks("pthread") ``` At first glance, it's actually just a collection of `set_xxx`/`add_xxx` configurations. For novices, you can completely not treat it as a lua script, just as an ordinary configuration file with some basic rules. Doesn't this look more like a configuration file? Actually, the description domain is a configuration file, similar to key/value configurations like json, so even novices who don't know lua at all can get started quickly. Moreover, for usual projects, configuring various project settings only through `set_xxx/add_xxx` is already sufficient. This is what was said at the beginning: 80% of the time, you can use the simplest configuration rules to simplify project configuration, improve readability and maintainability, which is very friendly to both users and developers, and more intuitive. What if we want to do conditional judgments for different platforms and architectures? No problem, the description domain supports conditional judgments and for loops in addition to basic configuration: ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") if is_plat("linux", "macosx") then add_links("pthread", "m", "dl") end ``` ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") for _, name in ipairs({"pthread", "m", "dl"}) do add_links(name) end ``` Doesn't this look a bit like lua? Although you can usually treat it as an ordinary configuration problem, xmake is after all based on lua, so the description domain still supports basic lua language features. :::note However, it should be noted that although the description domain supports lua script syntax, try not to write too complex lua scripts in the description domain, such as some time-consuming function calls and for loops. ::: And in the description domain, the main purpose is to set configuration items, so xmake does not fully open all module interfaces. Many interfaces are prohibited from being called in the description domain. Even some of the callable interfaces that are opened are completely read-only, non-time-consuming safe interfaces, such as: `os.getenv()` to read some regular system information, used for configuration logic control. :::note Also note that xmake.lua will be parsed multiple times, used to parse different configuration domains at different stages: such as: `option()`, `target()` and other domains. ::: Therefore, don't think about writing complex lua scripts in the description domain of xmake.lua, and don't call print in the description domain to display information, because it will be executed multiple times. Remember: it will be executed multiple times!!! ### Script Domain Restricting the description domain from writing complex lua, and various lua modules and interfaces can't be used? What to do? This is when the script domain comes into play. If users are already fully familiar with xmake's description domain configuration and feel that some special configuration maintenance for the project cannot be satisfied, then we can do more complex configuration logic in the script domain: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load(function (target) if is_plat("linux", "macosx") then target:add("links", "pthread", "m", "dl") end end) after_build(function (target) import("core.project.config") local targetfile = target:targetfile() os.cp(targetfile, path.join(config.buildir(), path.filename(targetfile))) print("build %s", targetfile) end) ``` Any script inside a function body like: `on_xxx`, `after_xxx`, `before_xxx`, etc., belongs to the script domain. In the script domain, users can do anything. xmake provides the import interface to import various built-in lua modules of xmake, and can also import user-provided lua scripts. We can implement any function you want in the script domain, and even write an independent project. For some script fragments, if they're not too bloated, writing them inline like above is sufficient. If you need to implement more complex scripts and don't want to fill up one xmake.lua, you can separate the scripts into independent lua files for maintenance. For example: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load("modules.test.load") on_install("modules.test.install") ``` We can place custom scripts in the directory corresponding to xmake.lua, `modules/test/load.lua` and `modules/test/install.lua`, for independent maintenance. A separate lua script file uses main as the main entry point, for example: ```lua -- We can also import some built-in modules or our own extension modules here for use import("core.project.config") import("mymodule") function main(target) if is_plat("linux", "macosx") then target:add("links", "pthread", "m", "dl") end end ``` In these independent lua scripts, we can also import various built-in modules and custom modules through [import](https://xmake.io) for use, just like writing lua or java normally. For different stages of the script domain, `on_load` is mainly used when the target is loaded to do some dynamic configuration. Unlike the description domain, this will only be executed once!!! There are many other stages, such as: `on/after/before`\_`build/install/package/run`, etc. We will describe them in detail below. ### import #### Importing Extension Modules Before explaining each script domain, let's briefly introduce xmake's module import and usage. xmake uses import to introduce other extension modules, as well as user-defined modules. It can be used in the following places: * Custom scripts ([on\_build](https://xmake.io), [on\_run](https://xmake.io) ..) * Plugin development * Template development * Platform extensions * Custom tasks The import mechanism is as follows: 1. First import from the current script directory 2. Then import from extension libraries The syntax rules for import: Based on `.` library path rules, for example: ```lua import("core.base.option") import("core.base.task") function main() -- Get parameter options print(option.get("version")) -- Run tasks and plugins task.run("hello") end ``` Import custom modules from 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 public interfaces inside. Private interfaces are marked with the `_` prefix, indicating that they will not be exported and will not be called externally. In addition to the current directory, we can also import libraries from 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() -- This way we can use p to call the platform module's plats interface to get a list of all platforms supported by xmake print(p.plats()) end ``` Version 2.1.5 added two new properties: `import("xxx.xxx", {try = true, anonymous = true})` If try is true, if the imported module does not exist, it will only return nil and will not throw an exception to interrupt xmake. If anonymous is true, the imported module will not be introduced into the current scope, and the import interface will only return the imported object reference. ### Testing Extension Modules One way is that we can directly call print in scripts like on\_load to print the call result information of modules for testing and verification. However, xmake also provides the `xmake lua` plugin for more flexible and convenient script testing. #### Running Specified Script Files For example, we can directly specify a lua script to load and run. This is a good way to quickly test some interface modules and verify some of your ideas. Let's write a simple lua script first: ```lua function main() print("hello xmake!") end ``` Then just run it directly: ```console $ xmake lua /tmp/test.lua ``` #### Directly Calling Extension Modules All built-in module and extension module interfaces can be directly called through `xmake lua`, for example: ```console $ xmake lua lib.detect.find_tool gcc ``` The above command directly calls the `import("lib.detect.find_tool")` module interface for quick execution. #### Running Interactive Commands (REPL) Sometimes in interactive mode, running commands is more convenient for testing and verifying some modules and APIs, and is more flexible, without needing to write an additional script file to load. Let's first see how to enter interactive mode: ```console # Execute without any parameters to enter $ xmake lua > # Expression calculation > 1 + 2 3 # Assignment and printing variable values > a = 1 > a 1 # Multi-line input and execution > for _, v in pairs({1, 2, 3}) do >> print(v) >> end 1 2 3 ``` We can also import extension modules through `import`: ```console > task = import("core.project.task") > task.run("hello") hello xmake! ``` If you want to cancel multi-line input midway, just enter the character: `q` ```console > for _, v in ipairs({1, 2}) do >> print(v) >> q <-- Cancel multi-line input, clear previous input data > 1 + 2 3 ``` ### target:on\_load #### Custom Target Loading Script When the target is initialized and loaded, this script will be executed. You can do some dynamic target configuration inside 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) ``` In `on_load`, you can dynamically add various target properties through `target:set`, `target:add`. All `set_`, `add_` configurations in the description domain can be dynamically configured this way. In addition, we can call some interfaces of target to get and set some basic information, for example: | target Interface | Description | | ----------------------------------- | ---------------------------------------------------------------- | | target:name() | Get target name | | target:targetfile() | Get target file path | | target:targetkind() | Get target build type | | target:get("defines") | Get target macro definitions | | target:get("xxx") | Other target information set through `set_/add_` interfaces can be obtained through this interface | | target:add("links", "pthread") | Add target settings | | target:set("links", "pthread", "z") | Overwrite target settings | | target:deps() | Get all dependent targets of the target | | target:dep("depname") | Get specified dependent target | | target:opts() | Get all associated options of the target | | target:opt("optname") | Get specified associated option | | target:pkgs() | Get all associated dependency packages of the target | | target:pkg("pkgname") | Get specified associated dependency package | | target:sourcebatches() | Get all source file lists of the target | ### target:on\_link #### Custom Link Script This is a new interface added after v2.2.7, used for customized processing of the target's linking process. ```lua target("test") on_link(function (target) print("link it") end) ``` ### target:on\_build #### Custom Build Script Override the target's default build behavior to implement a custom compilation process. Generally, this is not needed unless you really need to do some compilation operations that xmake doesn't provide by default. You can override it in the following way to customize compilation operations: ```lua target("test") -- Set custom build script 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, it specifies which `platform|architecture` this script needs to be executed under, and supports pattern matching, such as `arm*` matching all arm architectures. Of course, you can also only set the platform without setting the architecture, which means matching the specified platform and executing the script: ```lua target("test") on_build("windows", function (target) print("build for windows") end) ``` Note: Once you set your own build process for this target, xmake's default build process will no longer be executed. ### target:on\_build\_file #### Custom Build Script, Implementing Single File Build Through this interface, you can hook the specified target's built-in build process and re-implement the compilation process for each source file: ```lua target("test") set_kind("binary") add_files("src/*.c") on_build_file(function (target, sourcefile, opt) end) ``` ### target:on\_build\_files #### Custom Build Script, Implementing Multi-file Build Through this interface, you can hook the specified target's built-in build process and replace the compilation process for a batch of source files of the same type: ```lua target("test") set_kind("binary") add_files("src/*.c") on_build_files(function (target, sourcebatch, opt) end) ``` After setting this interface, the files in the corresponding source file list will not appear in the custom target.on\_build\_file, because this is an inclusion relationship. Where sourcebatch describes this batch of source files of the same type: * `sourcebatch.sourcekind`: Get the type of this batch of source files, such as: cc, as, .. * `sourcebatch.sourcefiles()`: Get source file list * `sourcebatch.objectfiles()`: Get object file list * `sourcebatch.dependfiles()`: Get corresponding dependency file list, containing compilation dependency information in source files, such as: xxx.d ### target:on\_clean #### Custom Clean Script Override the target's `xmake [c|clean]` clean operation to implement a custom clean process. ```lua target("test") -- Set custom clean script on_clean(function (target) -- Only delete target file os.rm(target:targetfile()) end) ``` ### target:on\_package #### Custom Package Script Override the target's `xmake [p|package]` package operation to implement a custom package process. If you want to package a specified target into your desired format, you can customize it through this interface. ```lua target("demo") set_kind("shared") add_files("jni/*.c") on_package(function (target) os.exec("./gradlew app:assembleDebug") end) ``` Of course, this example is a bit old. It's just used here to illustrate the usage. Now xmake provides a dedicated [xmake-gradle](https://github.com/xmake-io/xmake-gradle) plugin for better integration with gradle. ### target:on\_install #### Custom Install Script Override the target's `xmake [i|install]` install operation to implement a custom install process. For example, install the generated apk package. ```lua target("test") -- Set custom install script, automatically install apk file on_install(function (target) -- Use adb to install the packaged apk file os.run("adb install -r ./bin/Demo-debug.apk") end) ``` ### target:on\_uninstall #### Custom Uninstall Script Override the target's `xmake [u|uninstall]` uninstall operation to implement a custom uninstall process. ```lua target("test") on_uninstall(function (target) ... end) ``` ### target:on\_run #### Custom Run Script Override the target's `xmake [r|run]` run operation to implement a custom run process. For example, run the installed apk program: ```lua target("test") -- Set custom run script, automatically run the installed app program 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) ``` ### before\_xxx and after\_xxx It should be noted that all interfaces of target:on\_xxx override the internal default implementation. Usually we don't need to completely rewrite it, just hook some of our own logic. In that case, we can use the `target:before_xxx` and `target:after_xxx` series scripts. All on\_xxx have corresponding before\_ and after\_xx versions, with exactly the same parameters, for example: ```lua target("test") before_build(function (target) print("") end) ``` ### Built-in Modules In custom scripts, in addition to using the import interface to import various extension modules for use, xmake also provides many basic built-in modules, such as: os, io and other basic operations, to achieve more cross-platform processing of system interfaces. #### os.cp os.cp behaves similarly to the `cp` command in shell, but is more powerful. It not only supports pattern matching (using lua pattern matching), but also ensures recursive directory creation of the destination path and supports xmake's built-in variables. For example: ```lua os.cp("$(scriptdir)/*.h", "$(buildir)/inc") os.cp("$(projectdir)/src/test/**.h", "$(buildir)/inc") ``` The above code will: copy all header files under the current `xmake.lua` directory and all header files under the project source test directory to the `$(buildir)` output directory. Among them, `$(scriptdir)`, `$(projectdir)` and other variables are xmake's built-in variables. For specific details, see the relevant documentation: [Built-in Variables](https://xmake.io/mirror/zh-cn/manual/builtin_variables.html). The matching patterns in `*.h` and `**.h` are similar to those in [add\_files](https://xmake.io). The former is single-level directory matching, and the latter is recursive multi-level directory matching. The above copy will expand all files and copy them to the specified directory, losing the source directory hierarchy. If you want to maintain the original directory structure when copying, you can set the rootdir parameter: ```lua os.cp("src/**.h", "/tmp/", {rootdir = "src"}) ``` The above script can copy all sub-files under src while maintaining the directory structure according to the `src` root directory. Note: Try to use the `os.cp` interface instead of `os.run("cp ..")`, which can better ensure platform consistency and achieve cross-platform build descriptions. #### os.run This interface will quietly run native shell commands, used to execute third-party shell commands, but will not echo output, only highlight error information after errors. This interface supports parameter formatting and built-in variables, for example: ```lua -- Format parameters os.run("echo hello %s!", "xmake") -- List build directory files os.run("ls -l $(buildir)") ``` #### os.execv This interface, compared to os.run, will also echo output during execution, and parameters are passed in as a list, which is more flexible. ```lua os.execv("echo", {"hello", "xmake!"}) ``` In addition, this interface also supports an optional parameter for passing settings: redirect output, execution environment variable settings, for example: ```lua os.execv("echo", {"hello", "xmake!"}, {stdout = outfile, stderr = errfile, envs = {PATH = "xxx;xx", CFLAGS = "xx", curdir = "/tmp"}} ``` Among them, stdout and stderr parameters are used to pass redirected output and error output. You can directly pass file paths, or pass file objects opened by io.open. In addition, if you want to temporarily set and modify some environment variables during this execution, you can pass the envs parameter. The environment variable settings inside will replace the existing settings, but will not affect the outer execution environment, only the current command. We can also get all current environment variables through the `os.getenvs()` interface, then modify part of them and pass them into the envs parameter. In addition, you can also set the curdir parameter to modify the working directory of the child process during execution. Related similar interfaces include os.runv, os.exec, os.execv, os.iorun, os.iorunv, etc. For example, os.iorun can get the output content of the run. For specific details and differences of this, and more os interfaces, please see: [os Interface Documentation](https://xmake.io) for details. #### io.readfile This interface reads all content from the specified path file. We can directly read the entire file content without opening the file, which is more convenient, for example: ```lua local data = io.readfile("xxx.txt") ``` #### io.writefile This interface writes all content to the specified path file. We can directly write the entire file content without opening the file, which is more convenient, for example: ```lua io.writefile("xxx.txt", "all data") ``` #### path.join This interface implements cross-platform path concatenation operations, appending multiple path items. Due to the path differences between `windows/unix` styles, using APIs to append paths is more cross-platform, for example: ```lua print(path.join("$(tmpdir)", "dir1", "dir2", "file.txt")) ``` The above concatenation is equivalent to: `$(tmpdir)/dir1/dir2/file.txt` on unix, and equivalent to: `$(tmpdir)\\dir1\\dir2\\file.txt` on windows. For more built-in module details, see: [Built-in Module Documentation](https://xmake.io) --- --- url: /posts/quickstart-2-create-and-build-project.md --- Xmake is a lightweight modern C/C++ project build tool based on Lua. Its main features are: easy to use syntax, more readable project maintenance, and a consistent build experience across platforms. This article focuses on how to create a xmake-based project and compilation operations. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io/) ### Creating an empty project Xmake provides the `xmake create` command, which makes it easy to quickly create empty projects in various languages such as c/c++, swift, objc, such as: ```bash $ xmake create test create test ...   [+]: xmake.lua   [+]: src/main.cpp   [+]: .gitignore create ok! ``` By default, a c++ hello world project is created, and a xmake.lua is generated in the root directory to describe the project's build rules. ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") ``` This is a very simple xmake.lua description, `target("test")` defines a sub-project module test, each target will generate a corresponding object file, the binary type here, specify to create a basic executable file. The top `mode.debug` and `mode.release` rule settings are optional, but we usually recommend adding them so that the default two common build modes are available: debug and release. ### Execution compilation Usually if we just compile the executable file of the current host environment, we only need to execute the xmake command: ```bash $ xmake checking for the Xcode directory ... /Applications/Xcode.app assessment for the SDK version of Xcode ... 10.15 [0%]: ccache compiling.release src/main.cpp [100%]: linking.release test ``` Xmake will detect the existing build environment of the current environment by default, such as the author's current xcode environment, and then compile by default release mode. If the `mode.release` rule is set, it will take effect. ### Compile mode switch And if we want to cut to `mode.debug` compile, we only need to: ```bash $ xmake f -m debug $ xmake ``` Among them, `xmake f` is a shorthand for the `xmake config` command, which is used to quickly switch the configuration. If you start with it, it is more convenient to use shorthand. The shorthand for more commands can be executed by `xmake --help`. ### Create another template project `xmake create` can also be used to create various other types of engineering projects, we can type `xmake create --help` to see: ```bash $ xmake create --help Usage: $xmake create [options] [target] Create a new project. Options:                                                 -l LANGUAGE, --language=LANGUAGE The project language (default: c++)                                                - c++                                                - go                                                - dlang                                                - cuda                                                - rust                                                - swift                                                - objc                                                - c                                                - objc++     -t TEMPLATE, --template=TEMPLATE Select the project template id or name of the given language.                                            (default: console)                                                - console: c++, go, dlang, cuda, rust, swift, objc, c, objc++                                                - qt.console: c++                                                - qt.quickapp: c++                                                - qt.quickapp_static: c++                                                - qt.shared: c++                                                - qt.static: c++                                                - qt.widgetapp: c++                                                - qt.widgetapp_static: c++                                                - shared: c++, dlang, cuda, c                                                - static: c++, go, dlang, cuda, rust, c                                                - tbox.console: c++, c                                                - tbox.shared: c++, c                                                - tbox.static: c++, c                                                 Target Create the given target.                                            Uses the project name as target if not exists. ``` From the help menu above, we can probably understand that the engineering language can be specified by `-l/--language`, and `-t/--template` is used to specify the type of project template. For example, let's create a static library project based on c: ```bash $ xmake create -l c -t static test create test ...   [+]: xmake.lua   [+]: src/interface.c   [+]: src/interface.h   [+]: src/test.c   [+]: src/main.cpp   [+]: .gitignore create ok! ``` We can also create qt-based quickapp projects: ```bash $ xmake create -l c++ -t qt.quickapp test create test ...   [+]: xmake.lua   [+]: src/interface.c   [+]: src/main.qml   [+]: src/interface.h   [+]: src/test.c   [+]: src/main.cpp   [+]: src/qml.qrc   [+]: .gitignore create ok! ``` In addition to the c/c++ project, xmake also supports project compilation in other languages, but xmake focuses on c/c++. Other languages ​​are supported mainly to support mixed compilation with c/c++. After all, other languages ​​have official rust to rust. Provide a better build solution. But we can still use xmake to try to compile them: ```bash $ xmake create -l rust test create test ...   [+]: xmake.lua   [+]: src/main.rs   [+]: .gitignore create ok! ``` ```bash $ xmake checking for the architecture ... x86_64 checking for the Xcode directory ... /Applications/Xcode.app assessment for the SDK version of Xcode ... 10.15 [0%]: linking.release test ``` --- --- url: /posts/quickstart-3-run-and-debug.md --- Xmake is a lightweight modern C/C++ project build tool based on Lua. Its main features are: easy to use syntax, more readable project maintenance, and a consistent build experience across platforms. This article mainly explains in detail how to load and run the compiled target program, and how to debug. * [Project Source](https://github.com/xmake-io/xmake) * [Official Documents](https://xmake.io/) ### Run build target xmake also provides a run command to directly run the generated executable file for quick and easy testing, for example: ```bash $ xmake run hello xmake! ``` #### Adding runtime environment variables We can also add environment variables to set the default running target program through the `add_runenvs` interface in xmake.lua. Therefore, for PATH, it is very convenient to append values through this interface, and this interface supports multi-value setting, so it is usually used to set multi-value env with path sep. . ```lua target("test") set_kind("binary") add_files("src/*.c") add_runenvs("PATH", "/tmp/bin", "xxx/bin") add_runenvs("LD_LIBRARY_PATH", "/tmp/lib", "xxx/lib") ``` For more description of this interface, you can see the documentation: [add\_runenvs interface documentation](https://xmake.io) #### Custom run logic If the simple environment settings and the default loading and running rules do not meet the requirements, we can customize the on\_run script to achieve more complex running logic: For example, run the installed apk program: ```lua target("test") -- ... -- Set a custom run script, automatically run the installed app, and automatically obtain device output information on_run(function(target) os.run("adb shell am start -n com.demo/com.demo.DemoTest") os.run("adb logcat") end) ``` ### Debugger #### Command line debugging We can also pass `-d` parameters, call debugger programs such as gdb/lldb, load the target file for debugging: ```bash $ xmake run -d ``` xmake will use the debugger that comes with the system to load the program.currently it supports: lldb, gdb, windbg, vsjitdebugger, ollydbg and other debuggers. ```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] $ ``` #### Breakpoint debugging with vscode We can also use the [xmake-vscode](https://github.com/xmake-io/xmake-vscode) plugin to cooperate with vscode to implement breakpoint debugging support for c/c++ projects. In addition, we need to rely on the c++ plug-in of vscode for debugging support, but since developing c/c++ programs, this plug-in is almost necessary, so there is not much problem. Even if this plugin is not installed, xmake-vscode will load the system debuggers such as lldb/gdb/vsjitdebugger, and directly load and debug. --- --- url: /posts/quickstart-4-basic-project-settings.md --- Xmake is a lightweight and modern C/C++ project build tool based on Lua. Its main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. This article mainly explains in detail how to write some commonly used basic xmake.lua description configurations to achieve some simple C/C++ project build management. For most small projects, these configurations are completely sufficient. In the later advanced tutorials in this series, I will explain in detail how to use some advanced features to configure the project more flexibly and customized. * [Project Source](https://github.com/xmake-io/xmake) * [Official Documents](https://xmake.io/) ### A simplest example One line of description compiles all c source files in the src directory, and then generates an executable file named demo. ```lua target("demo", {kind = "binary", files = "src/*.c"}) ``` The above is a condensed version. Generally, we recommend the following expansion method: ```lua target("demo") set_kind("binary") add_files("src/*.c") ``` The two are completely equivalent. If the configuration is short, it can be completely reduced to one line, and splitting into multiple lines is more convenient and flexible. If there is no special purpose, we will use the second paragraph. ### Configure project target type There are three main types of object files generated by common C/C++ projects: executable programs, static libraries, and dynamic libraries. We can set it through `set_kind()` configuration, corresponding to: binary, static, shared For example, if we want to compile the dynamic library, we just need to modify the kind: ```lua target("demo") set_kind("shared") add_files("src/*.c") ``` ### Add macro definition The compilation macro settings are used by most c/c++ projects. Generally, if we set compilation flags to be passed to gcc/clang, we need to configure: `-DXXX` In xmake, the `add_defines()` built-in interface is provided for configuration: ```lua target("demo") set_kind("shared") add_files("src/*.c") add_defines("XXX") ``` ### Conditional configuration What if we want to set different macro switches on different compilation platforms? We can easily implement it using lua's built-in if statement: ```lua target("demo") set_kind("shared") add_files("src/*.c") add_defines("XXX") if is_plat("linux", "macosx") then add_defines("YYY") end ``` We judge by `is_plat()`. If the current compilation target platform is linux or macosx, then the target will additionally add the `-DYYY` macro definition. ### Global configuration All the configurations under `target("demo")` belong to the target subdomain of demo, not global, so you will see that the indentation is usually added to the configuration to highlight the scope of influence. . Generally, if multiple targets are defined consecutively, the next target definition will automatically end the scope of the previous target. The configuration of each target is completely independent and does not interfere with each other: ```lua target("test1") set_kind("shared") add_files("src/*.c") add_defines("TEST1") target("test2") set_kind("shared") add_files("src/*.c") add_defines("TEST2") ``` For example, the above configuration has two targets, each with its own independent macro definition: `TEST1` and` TEST2`. So, we need to set a common macro definition for these two targets. How should we configure it? `add_defines("TEST")` is configured under each target? Of course it can, but this is a bit redundant, and it will be difficult to maintain if you configure more. In fact, we only need to place it in the global root scope: ```lua -- global settings add_defines("TEST") if is_arch("arm64", "armv7") then add_defines("ARM") end target("test1") set_kind("shared") add_files("src/*.c") add_defines("TEST1") target("test2") set_kind("shared") add_files("src/*.c") add_defines("TEST2") ``` All configurations outside the target belong to the global configuration. We can also call `target_end()` to forcibly end the target subdomain and switch back to the global scope: ```lua target("test1") set_kind("shared") add_files("src/*.c") add_defines("TEST1") target_end() -- global settings add_defines("TEST") if is_arch("arm64", "armv7") then add_defines("ARM") end target("test2") set_kind("shared") add_files("src/*.c") add_defines("TEST2") target_end() ``` ### Add compilation options If there are some compilation options, xmake does not provide built-in api settings, then we can degenerate to `add_cflags`,` add_cxflags`, `add_cxxflags` to set, However, this requires the user to determine the compilation platform, because not all compilation flags are supported on every platform. such as: ```lua add_cflags("-g", "-O2", "-DDEBUG") if is_plat("windows") then add_cflags("/MT") end ``` 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 them internally to option values supported by the corresponding compiler. The user does not need to worry about its compatibility. If other compilers do not have corresponding matching values, xmake will automatically ignore the compiler settings. We can also force the automatic detection of flags through the force parameter, and pass it directly to the compiler, even if the compiler may not support it, it will be set: ```lua add_cflags("-g", "-O2", {force = true}) ``` So how do you know which flags failed to be ignored, you can see with `-v` compilation, such as: ```bash $ xmake -v checking for the /usr/bin/xcrun -sdk macosx clang ... ok checking for the flags(-Oz) ... ok checking for the flags(-Wno-error=deprecated-declarations) ... ok checking for the flags(-fno-strict-aliasing) ... ok checking for the flags(-Wno-error=expansion-to-defined) ... no ``` Finally, note the differences between these three APIs: * `add_cflags`: add only C code-related compilation flags * `add_cxflags`: add C/c++ code related flags * `add_cxxflags`: add only c++ code-related compilation flags ### Add library related settings For the integrated use of a C/c++ library, you usually need to set the header file search directory, link library name, and library search directory, such as: ```lua target("test") set_kind("binary") add_links("pthread") add_includedirs("/usr/local/include") add_linkdirs("/usr/local/lib") ``` Generally, in order to ensure the dependency order of the linked libraries, the system library links are usually backward. We use `add_syslinks()` to set the system library links specifically, and `add_links()` is usually used for non-system library links: ```lua target("test") set_kind("binary") add_links("A", "B") add_syslinks("pthread") ``` In the above configuration, we added two third-party link libraries: A, B, and the system library pthread. The complete link sequence is: `-lA -lB -lpthread`, and syslinks will be placed at the end. If you are unsure of the actual linking order, we can execute `xmake -v` compilation to see the complete link parameter command line. ### Setting the language standard The c standard and c++ standard can be set at the same time, for example: ```lua -- set the c code standard: c99, c++ code standard: c++ 11 set_languages("c99", "c++11") ``` Note: The specified standard is not set, and the compiler will compile according to this standard. After all, each compiler supports different strengths, but xmake will try its best to adapt to the current standard of the compilation tool. For example: the compiler for windows vs does not support compiling c code according to the c99 standard, only c89 is supported, but xmake supports it as much as possible, so after setting the c99 standard, xmake will compile c code according to c++ code mode, which solves the problem of c code compiling c99 under windows. ### Set compilation optimization xmake provides several built-in compilation optimization configurations: none, fast, faster, fastest, smallest, aggressive, to achieve various levels of compilation optimization. ```lua set_optimize("fastest") ``` If you set it through flags, you also need to consider the different compilation options of different compilers. Xmake has an internal mapping process for it, which greatly facilitates users to provide cross-platform. If you want to view detailed mapping rules, you can go to the official documentation of xmake to check it: [Compile Optimization Settings](https://xmake.io) ### Debug and Release Mode Even though xmake provides `set_optimize` to simplify the complicated configuration of different compilers, for different compilation modes: debug/release, you still have to make some tedious judgments and configurations yourself: ```lua if is_mode("debug") then set_symbols("debug") set_optimize("none") end if is_mode("release") then set_symbols("hidden") set_strip ("all") if is_plat("iphoneos", "android") then set_optimize("smallest") else set_optimize("fastest") end end ``` These seemingly commonly used settings, if each project is repeated, it is also very tedious, resulting in xmake.lua not concise and readable, so xmake provides some commonly used built-in rules to simplify the setting: ```lua add_rules("mode.release", "mode.debug") ``` Only this line is needed, the effect is completely the same, and the user can also do some additional custom configurations to rewrite based on this: ```lua add_rules("mode.release", "mode.debug") if is_mode("release") then set_optimize("fastest") end ``` For example, I want to force fastest compilation optimization in release mode. Now that we have a mode configuration, how do we switch to debug mode compilation? (Default is release compilation) answer: ```lua xmake f -m debug; xmake ``` ### Add source files Finally, we introduce one of the most common and powerful settings of xmake, which is the configuration management of compiled source files: `add_files()`. We can use this interface to add various source files supported by xmake, such as: c/c++, asm, objc, swift, go, dlang and other source files, and even: `.obj`, `.a/.lib`, etc. Binary objects and library files. E.g: ```lua add_files("src/test_*.c") add_files("src/xxx/**.cpp") add_files("src/asm/*.S", "src/objc/**/hello.m") ``` The wildcard `*` matches files in the current directory, while `**` matches files in multiple directories. The use of `add_files` is actually quite flexible and convenient. Its matching pattern borrows the style of premake, but it is improved and enhanced. This makes it possible not only to match files, but also to filter out a batch of files with a specified pattern 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 exclude 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 files after the delimiter `|` are all files that need to be excluded. These files also support matching patterns, and multiple filtering patterns can be added at the same time, as long as they are separated by `|`. . One of the benefits of supporting filtering some files when adding files is that it can provide a basis for subsequent addition of files based on different switch logic. Note: In order to make the description more concise, the filtering description after `|` is based on a pattern: the directory before `*` in `src/*.cpp`. So the above example filters the files under src, which should be noted. After 2.1.6, `add_files` has been improved to support more fine-grained compilation option control based on files, for example: ```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 the same as those of the target, and these files will inherit the target's general configuration` -DTEST1`. After version 2.1.9, it supports adding unknown code files. By setting rule customization rules, these files can be custom built, for example: ```lua target("test") -- ... add_files("src/test/*. md", {rules = "markdown"}) ``` And after version 2.1.9, you can use the force parameter to forcibly disable the automatic detection of cxflags, cflags and other compilation options, and pass it directly to the compiler, even if the compiler may not support it, it will be set: ```lua add_files("src/*.c", {force = {cxflags = "-DTEST", mflags = "-framework xxx"}}) ``` ### Delete the specified source file Now that we ’ve talked about adding source files, how to delete them, let ’s just drop in. We only need to use the `del_files()` interface to delete the specified files from the list of files added by the `add_files` interface. : ```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 achieved by `add_files("src/*.c|test.c")`. But this approach is more flexible. For example, we can use conditional judgment to control which files are deleted, and this interface also supports `add_files` matching mode, filtering mode, and batch 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 order, and delete one by `del_files("src/subdir/*.c|xxx.c")` Batch file, And exclude `src/subdir/xxx.c` (that is, do not delete this file). --- --- url: /posts/quickstart-5-build-android.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. This article mainly explains in detail how to compile libraries and executable programs that can run under android through xmake. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### Ready to work First of all, we need to prepare the ndk toolchain necessary for compiling the android native library. If you haven’t, you can download and decompress it from the official websit: [Android NDK](https://developer.android.com/ndk) If you want to get better backward compatibility, you can choose the r16 version, because this is the last version that supports armeabi. If there is no special requirement, you can download the latest version directly. ### NDK integration and compilation #### Manually configure the NDK We only need to pass the decompressed ndk directory path to xmake to complete the configuration, and we can compile directly, for example: ```bash $ xmake f -p android --ndk=~/downloads/android-ndk-r19c $ xmake ``` Among them, `-p android` is used to switch to the android platform, because if you do not specify a platform, the target program of the current host platform will be compiled by default. Generally, if there is no special requirement, the above configuration can complete the compilation of the android native program. Currently, xmake has built-in support for the generation of three types of target files: binary, static, and shared, which correspond to executable programs and .a static libraries. .so dynamic library. #### Global configuration of NDK paths The `xmake f/config` command is only for the configuration of the current project. If you often need to set the ndk path again during cross-platform compilation and configuration switching, it is still a little tedious. We can set it through the `xmake g/global` global configuration command to ensure it takes effect permanently. ```bash $ xmake g --ndk=~/xxx/android-ndk-r19c ``` We can also ensure the permanent effect by setting the `ANDROID_NDK_HOME` global environment variable, which is similar to the effect of the above command configuration. #### Automatic detection of NDK paths Generally, even if the ndk path is not configured, xmake will still try to detect some common paths by default. For example, under macos, it will automatically detect whether the following paths exist: ``` ~/Library/Android/sdk/ndk-bundle ``` This is the SDK directory automatically created by android studio after downloading the Mac, and the common place for the ndk. Or try to probe from the environment variable `ANDROID_NDK_HOME`, if it exists. If it can be detected, there is no need to configure it manually. ### C++ STL library configuration switch First, let’s introduce the three stl library versions provided by ndk. * stlport: the stl library built in early ndk, now basically obsolete * gnustl: stl library mainly used before ndk r16b, but since r16b, it has also been removed by google * llvm-c++: newer ndk built-in stl libraries after r16b Therefore, when we compile the android library, we need to choose stl and choose the appropriate ndk version according to our needs. Xmake usually uses the llvm-c++ library by default if possible. If it finds that the current ndk version is older, it will try to degrade Go to gnustl. Users can also manually modify the version of the stl library, for example: ```bash $ xmake f -p android --ndk=xxxx --ndk_cxxstl=gnustl_shared ``` Specifically, for the configuration value of the ndk\_cxxstl option, you can type help to view, `xmake f --help`, mainly: * `llvmstl_static` * `llvmstl_shared` * `gnustl_static` * `gnustl_shared` * `stlport_static` * `stlport_shared` ### API Version Settings If during the compilation process, some libc library symbols are not found, it is usually possible that the api version is not set correctly, because some libc functions exist only in higher version apis. At this time, we can solve it by trying to manually modify the api version: ```bash $ xmake f -p android --ndk=xxx --ndk_sdkver=16 ``` ### Switch compilation architecture At present xmake provides configuration of these architectures `armv7-a`,` arm64-v8a`, `armv5te`,` mips`, `mips64`,` i386`, `x86_64`. If arch is not specified, then armv7 will be used by default. Architecture. Manually modify the arch as follows: ```bash $ xmake f -p android --ndk=xxx -a arm64-v8a ``` ### Android related configuration settings If the project needs to configure some compilation settings unique to the android platform, such as adding specific macro switches, link libraries, etc., you can use `is_plat("android")` to determine the processing in xmake.lua. ```lua target ("test")     set_kind ("shared")     add_files ("src/*. c")     if is_plat ("android") then         add_defines ("ANDROID")         add_syslinks ("log")     end ``` ### FAQ #### What should I do if I cannot find some libc/stl library header files? You can try to modify the stl library version and api version to solve it. For example, ndk r16b recommends using the gnustl library, because this version of the llvmc++ library has just been integrated shortly, there are many problems, and it is easy to encounter various compilation problems during use. ```bash $ xmake f -p android --ndk=xxxx --ndk_cxxstl=gnustl_shared --ndk_sdkver=16 ``` #### The compiled executable does not run on the device? Usually the api version is set too high, causing incompatibility issues, you can try to reduce the api version. --- --- url: /posts/quickstart-6-build-qt-project.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. xmake fully supports the maintenance and building of Qt5 projects. This article will guide you through how to maintain various types of Qt projects with xmake. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### Preface Qt is a cross-platform C++ graphical user interface application development framework developed by Qt Company in 1991. It has its own IDE program: Qt Creator, and its own build program: qmake. It seems that newer versions are planning to fully switch to cmake for maintenance. Despite this, xmake still provides support for Qt development. Combined with xmake-vscode/xmake-idea and other plugins, users can integrate and develop Qt programs in their familiar editors and IDEs, providing a consistent development experience across different platforms. ### Preparing the Build Environment First, we need to prepare the Qt development environment. If you haven't installed the Qt SDK yet, go to the Qt official website to log in and download the installation package: https://www.qt.io/, or pull the Qt source code yourself and compile a static version SDK and toolchain. Usually, if you use the official Qt SDK installation package and the installation directory uses the default path, xmake will try to detect it even without configuring the Qt SDK path. It can usually be detected. If it cannot be detected, we can try to configure it manually: ```bash $ xmake f --qt=/home/xxx/qtsdk ``` Or set it to a global path to avoid having to configure it every time you switch compilation modes: ```bash $ xmake g --qt=/home/xxx/qtsdk ``` ### Creating Template Projects xmake has built-in empty project templates for various Qt projects. We can quickly create them using the `xmake create` command. Note: Since xmake's master latest version, which is the unreleased v2.2.9 version, has upgraded Qt templates and build rules, this article mainly explains based on the latest version. The old templates and rules are still backward compatible. If you want to learn more, you can check the relevant documentation: [Qt Project Development Documentation](https://xmake.io) #### Creating QuickApp Applications Let's first create an empty quickapp project with qml. Just run the following command: ```bash $ xmake create -t qt.quickapp test create test ... [+]: xmake.lua [+]: src/main.qml [+]: src/main.cpp [+]: src/qml.qrc [+]: .gitignore create ok! ``` xmake will generate a Qt project with xmake.lua. The xmake.lua content is also very simple: ```lua target("test") add_rules("qt.quickapp") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/qml.qrc") ``` Except for adding source files, everything else is basically the same as the previous executable program project. The only difference is using the built-in Qt build rule `add_rules("qt.quickapp")` instead of `set_kind("binary")`. Actually, the `qt.quickapp` rule internally sets the binary type in the end, but additionally adds some build rules that only Qt needs, such as: specific links, flags, and includedirs, etc. Next, let's try to compile this project: ```bash $ xmake checking for the architecture ... x86_64 checking for the Xcode directory ... /Applications/Xcode.app checking for the SDK version of Xcode ... 10.15 checking for the Qt SDK directory ... /Users/ruki/Qt5.13.2/5.13.2/clang_64 checking for the Qt SDK version ... 5.13.2 [ 0%]: ccache compiling.release src/main.cpp [ 49%]: compiling.qt.qrc src/qml.qrc [100%]: linking.release test ``` The build rule for `*.qrc` files is also maintained in the `qt.quickapp` build rule, so only by setting this rule can qrc files be compiled normally. Finally, let's try to run it: ```bash $ xmake run ``` The running effect is as follows: ![](/assets/img/guide/qt_quickapp.png) #### Creating WidgetApp Applications Creating a widgetapp project is basically the same as the quickapp method above, just change the template name: ```bash $ xmake create -t qt.widgetapp test ``` The xmake.lua content inside also just changes the `qt.quickapp` rule to the `qt.widgetapp` rule. In addition, the UI description file changes from `.qrc` to `.ui`, with no other differences. ```lua target("qt_widgetapp") add_rules("qt.widgetapp") add_files("src/*.cpp") add_files("src/mainwindow.ui") add_files("src/mainwindow.h") -- Add header file with Q_OBJECT meta ``` The running effect is as follows: ![](/assets/img/guide/qt_widgetapp.png) #### Creating Statically Linked Applications By default, the SDK downloaded from the Qt official website is based on dynamic libraries. If the user uses a static version Qt SDK compiled from Qt source code, the created Qt project type must also correspond to the static version, because the two handle linking differently. For the template name, append `_static` to create: ```bash $ xmake create -t qt.widgetapp_static test ``` This creates a WidgetApp project based on a static QtSdk. The build rule inside will also be changed to `add_rules("qt.widgetapp_static")`, with no other differences. The same applies to QuickApp projects. #### Creating Other Qt Projects In addition to QuickApp and WidgetApp projects, xmake also supports the creation and compilation of other Qt projects, such as: console programs, static libraries and dynamic libraries based on Qt, etc. For specific project templates, we can check the template list in the help menu: ```bash $ xmake create --help Usage: $xmake create [options] [target] Create a new project. Options: -t TEMPLATE, --template=TEMPLATE Select the project template id or name of the given language. (default: console) - console: c++, go, dlang, cuda, rust, swift, objc, c, objc++ - qt.console: c++ - qt.quickapp: c++ - qt.quickapp_static: c++ - qt.shared: c++ - qt.static: c++ - qt.widgetapp: c++ - qt.widgetapp_static: c++ ``` For more information on other Qt projects, see xmake's official documentation: [Qt Project Build Documentation](https://xmake.io) ### Running and Breakpoint Debugging We can use the `xmake run -d` command to load gdb/lldb debugger, or use xmake-vscode plugin's breakpoint debugging support to develop and debug Qt programs. You can read the previous article: [Xmake Getting Started Tutorial 3, Running and Debugging Target Programs](https://tboox.org/cn/2019/11/09/quickstart-3-run-and-debug/) In addition, if it's a Windows platform, we can also generate a VS project and use VS's built-in debugging features for breakpoint debugging, which is more convenient: ```bash $ xmake project -k vsxmake ``` After generating the VS project based on xmake, open the VS project and click debug run: ![](/assets/img/manual/qt_vs.png) ### Developing Android Programs xmake currently fully supports compiling Android versions of Qt projects. The entire Qt project including xmake.lua is exactly the same as the previous examples, and no special settings are needed. What we need to do is simply switch to the Android compilation platform to compile it. However, since we need to generate an APK package, after executing xmake compilation, the Qt build rule will automatically perform a deploy step for the Android program, which is to call Qt's internal androiddeployqt program to generate the APK package. Therefore, in addition to the Android NDK, we also need to depend on the Android SDK. Specify it by setting the `--android_sdk` parameter: ```bash $ 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 ``` The above configuration and build process can easily compile the previous QuickApp and WidgetApp projects into Android Apps. In addition, the qt rule internally customizes the install program for the Android version, which can easily install the Qt APK to the device. ```bash $ xmake install installing appdemo ... installing build/android/armv7-a/release/appdemo.apk .. success install ok! ``` The effect after installation and running is as follows: ![](https://user-images.githubusercontent.com/151335/57430932-c7261000-7263-11e9-8886-eff07208d0d8.jpeg) For information on how to configure the Android build environment, read the previous article: [Xmake Getting Started Tutorial 5, Introduction to Android platform compilation](https://tboox.org/cn/2019/11/15/quickstart-5-build-android/) ### Editor and IDE Integration xmake also provides plugin integration support for major commonly used editors. With these plugins, you can develop and build Qt programs in your most familiar editor. #### Developing and Debugging Qt Programs in VSCode Plugin address: [xmake-vscode](https://github.com/xmake-io/xmake-vscode) #### Developing Qt Programs in Sublime Text Plugin address: [xmake-sublime](https://github.com/xmake-io/xmake-sublime) #### Developing Qt Programs in Idea/CLion/Android Studio Plugin address: [xmake-idea](https://github.com/xmake-io/xmake-idea) #### Developing and Debugging Qt Programs in Visual Studio This is the method mentioned above for integrating xmake by generating a VS project: ```bash $ xmake project -k vsxmake ``` After generating the VS project based on xmake, open the VS project and click debug run: ![](/assets/img/manual/qt_vs.png) For specific details, see the plugin documentation: [Using xmake to generate VS projects](https://xmake.io) --- --- url: /posts/quickstart-7-build-cuda-project.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. In this article, we will explain in detail how to build CUDA programs and mixed compilation with c/c++ programs through xmake. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### Preparing the Environment First, we need to install the CUDA Toolkit SDK tool provided by NVIDIA. For related instructions and installation documentation, please refer to the official documentation: [CUDA Toolkit Documentation](http://docs.nvidia.com/cuda/index.html). After downloading and installing the CUDA SDK, on macOS it will be installed to the `/Developer/NVIDIA/CUDA-x.x` directory by default. On Windows, you can find the corresponding SDK directory through the `CUDA_PATH` environment variable, and on Linux it will be installed to the `/usr/local/cuda` directory by default. Usually, xmake can automatically detect the default CUDA installation environment without any operation. You just need to execute the `xmake` command to automatically complete the compilation. Of course, if the SDK cannot be found, we can also manually specify the CUDA SDK environment directory: ```console $ xmake f --cuda=/usr/local/cuda-9.1/ ``` Or use the `xmake g/global` command to switch to global settings to avoid having to reconfigure every time you switch compilation modes. ```console $ xmake g --cuda=/usr/local/cuda-9.1/ ``` If you want to test xmake's detection support for the current CUDA environment, you can directly run: ```bash $ xmake l detect.sdks.find_cuda { linkdirs = { "/Developer/NVIDIA/CUDA-10.2/lib/stubs", "/Developer/NVIDIA/CUDA-10.2/lib" }, bindir = "/Developer/NVIDIA/CUDA-10.2/bin", sdkdir = "/Developer/NVIDIA/CUDA-10.2", includedirs = { "/Developer/NVIDIA/CUDA-10.2/include" } } ``` You can also help contribute related detection code [find\_cuda.lua](https://github.com/xmake-io/xmake/blob/master/xmake/modules/detect/sdks/find_cuda.lua) to improve xmake's detection mechanism. ### Creating a Project Next, we can create an empty project to quickly experience it. xmake comes with a CUDA project template. Just specify the corresponding language to create a CUDA project: ```bash $ xmake create -l cuda test create test ... [+]: xmake.lua [+]: src/main.cu [+]: .gitignore create ok! ``` The default created CUDA project is the simplest CUDA-based hello world project. Its source code structure is as follows: ``` ├── src │ └── main.cu └── xmake.lua ``` We can also take a brief look at the content in xmake.lua: ```lua -- define target target("test") 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") ``` As you can see, except for the most basic .cu source file addition, the only difference from other c/c++ projects is the addition of `add_cugencodes()` to set the gencodes required by CUDA. We will explain this in detail below. ### Compiling the Project After the project is created, you just need to simply execute xmake to complete the compilation. ```bash $ xmake [00%]: ccache compiling.release src/main.cu [99%]: devlinking.release test_gpucode.cu.o [100%]: linking.release test ``` Note: Starting from v2.2.7, xmake enables device-link build behavior by default. That is to say, an additional device-link step will be added during the compilation process: ```bash [100%]: devlinking.release test_gpucode.cu.o ``` According to the official statement, the main advantage of enabling device-link device code linking is that it can provide a more traditional code structure for your application, especially in C++, allowing you to control each build and link step under the premise of keeping the existing project structure unchanged, quickly enable GPU code, and achieve mixed compilation. For this, please refer to NVIDIA's official description: [Separate Compilation and Linking of CUDA C++ Device Code](https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/) If you want to disable the device-link build logic, you can set it to disable via `add_values("cuda.devlink", false)`. Of course, we can also try to run this CUDA program directly: ```bash $ xmake run ``` ### Project Settings And if the value inside is set to native, xmake will automatically detect the gencode corresponding to the CUDA device on the current host. #### add\_cuflags This interface is mainly used to add compilation options related to cu code. If we need some more customized flag settings, we can call `add_cuflags` to directly set more raw compilation options, just like `add_cxflags` in c/c++. For example: ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") ``` #### add\_culdflags This interface is mainly used to add CUDA device link options. As mentioned above, after 2.2.7, xmake's default build behavior for CUDA programs will use device-link. If you need to set some link flags at this stage, you can set them through this interface. Because the final program linking will use ldflags and will not call nvcc, it will be linked directly through gcc/clang and other c/c++ linkers. So the flag settings for the device-link independent link stage are completed through this interface. ```lua add_culdflags("-gencode arch=compute_30,code=sm_30") ``` #### add\_cugencodes The `add_cugencodes()` interface is actually a simplified encapsulation of the `add_cuflags("-gencode arch=compute_xx,code=compute_xx")` compilation flag settings. The mapping relationship between its internal parameter values and the actual flags is as follows: ```lua - compute_xx --> `-gencode arch=compute_xx,code=compute_xx` - sm_xx --> `-gencode arch=compute_xx,code=sm_xx` - sm_xx,sm_yy --> `-gencode arch=compute_xx,code=[sm_xx,sm_yy]` - compute_xx,sm_yy --> `-gencode arch=compute_xx,code=sm_yy` - compute_xx,sm_yy,sm_zz --> `-gencode arch=compute_xx,code=[sm_yy,sm_zz]` - native --> match the fastest cuda device on current host, eg. for a Tesla P100, `-gencode arch=compute_60,code=sm_60` will be added, if no available device is found, no `-gencode` flags will be added ``` For example: ```lua add_cugencodes("sm_30") ``` is equivalent to ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") add_culdflags("-gencode arch=compute_30,code=sm_30") ``` Isn't the above more concise? This is actually an auxiliary interface for simplified settings. And if we set the native value, xmake will automatically detect the CUDA device on the current host, then quickly match its corresponding gencode settings, and automatically append them to the entire build process. For example, if our host's current GPU is a Tesla P100 and can be automatically detected by xmake, then the following setting: ```lua add_cugencodes("native") ``` is equivalent to: ```lua add_cugencodes("sm_60") ``` ### CUDA/C/C++ Mixed Compilation For mixed compilation, we just need to continue adding the corresponding c/c++ code files through the `add_files` interface. Isn't it simple? ```lua target("test") set_kind("binary") add_files("src/*.cu") add_files("src/*.c", "src/*.cpp") add_cugencodes("native") ``` ### Compilation Settings When nvcc compiles internal c/c++ code, it actually calls the c/c++ compiler of the host environment to compile. For example, it defaults to using gcc/g++ on Linux, clang/clang++ on macOS, and cl.exe on Windows. If you want nvcc to use other compilers, such as using clang as the default c/c++ compiler on Linux, you need to specify the `--ccbin=` parameter setting. For this, please see: [compiler-ccbin](https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#file-and-path-specifications-compiler-bindir) In xmake, this is also supported. Just set `xmake f --cu-ccbin=clang` to switch to other compilers. There are two other compilation parameters related to CUDA. Let me briefly introduce them: ```bash xmake f --cu=nvcc --cu-ld=nvcc ``` Among them, `--cu` is used to set the compiler for .cu code. The default is nvcc. However, clang now also supports compilation of .cu code, so you can switch the setting to try it. `--cu-ld` is used to set the device-link linker, while the final overall program link process still uses `--ld` for linking. --- --- url: /posts/quickstart-8-switch-build-mode.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. In this article, we will explain in detail how to switch common build modes such as debug/release during the project build process, and how to customize other build modes. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### Debug and Release Modes Usually, if we create a project through the `xmake create` command, a build rule configuration line will be automatically added in xmake.lua, as follows: ```lua add_rules("mode.release", "mode.debug") target("hello") set_kind("binary") add_files("src/*.c") ``` Through the `add_rules` interface, we have added two commonly used built-in rules, release and debug, by default. They will attach some compilation flags related to the corresponding mode during compilation to enable optimization for release or debugging compilation. If we just execute the `xmake` command without additional configuration, it will default to release compilation, which is equivalent to: ```bash $ xmake f -m release $ xmake [ 0%]: ccache compiling.release src/main.cpp [100%]: linking.release test build ok! ``` If we want to switch to debug compilation mode, we just need to: ```bash $ xmake f -m debug $ xmake [ 0%]: ccache compiling.debug src/main.cpp [100%]: linking.debug test build ok! ``` The `-m/--mode=` parameter above is used to set the compilation mode, which will be associated with the `mode.release` and `mode.debug` rules. So, how are they associated? Let's first look at the internal implementation of these two rules: ```lua rule("mode.debug") after_load(function (target) if is_mode("debug") then if not target:get("symbols") then target:set("symbols", "debug") end if not target:get("optimize") then target:set("optimize", "none") end end end) rule("mode.release") after_load(function (target) if is_mode("release") then if not target:get("symbols") and target:targetkind() ~= "shared" then target:set("symbols", "hidden") end if not target:get("optimize") then if is_plat("android", "iphoneos") then target:set("optimize", "smallest") else target:set("optimize", "fastest") end end if not target:get("strip") then target:set("strip", "all") end end end) ``` As you can see, during the target loading phase, xmake will judge the user's parameter configuration for `xmake f --mode=xxx`. If it obtains debug mode through the `is_mode()` interface, it will disable related optimizations and enable symbol output. If it's release mode, it will enable compilation optimization and strip all debugging symbols. ### Customized Mode Configuration Of course, the compilation configurations set by these two built-in rules by default can only meet the regular needs of most scenarios. If users want to customize some personal compilation configurations in different compilation modes, they need to make judgments in xmake.lua themselves. For example, if we want to enable debugging symbols in release mode as well, we just need to: ```lua if is_mode("release") then set_symbols("debug") end ``` Or add some additional compilation flags: ```lua if is_mode("release") then add_cflags("-fomit-frame-pointer") end ``` Note: If the user's own configuration conflicts with the built-in configuration of `mode.release`, the user's settings will take priority. Of course, we can also completely avoid adding default configuration rules through `add_rules("mode.debug", "mode.release")`, letting users completely control the mode configuration themselves: ```lua -- If the current compilation mode is debug if is_mode("debug") then -- Add DEBUG compilation macro add_defines("DEBUG") -- Enable debugging symbols set_symbols("debug") -- Disable optimization set_optimize("none") end -- If it's release or profile mode if is_mode("release", "profile") then -- If it's release mode if is_mode("release") then -- Hide symbols set_symbols("hidden") -- Strip all symbols set_strip("all") -- Omit frame pointer add_cxflags("-fomit-frame-pointer") add_mxflags("-fomit-frame-pointer") -- If it's profile mode else -- Enable debugging symbols set_symbols("debug") end -- Add extended instruction sets add_vectorexts("sse2", "sse3", "ssse3", "mmx") end ``` ### Other Built-in Mode Rules Through the example above, we see that in addition to debug/release modes, there's also a profile mode configuration judgment. Actually, xmake also provides corresponding built-in modes. Let's see what else there is: #### mode.debug Add debug compilation mode configuration rules to the current project's xmake.lua, for example: ```lua add_rules("mode.debug") ``` Equivalent to: ```lua if is_mode("debug") then set_symbols("debug") set_optimize("none") end ``` We can switch to this compilation mode by: `xmake f -m debug`. #### mode.release Add release compilation mode configuration rules to the current project's xmake.lua, for example: ```lua add_rules("mode.release") ``` Equivalent to: ```lua if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end ``` We can switch to this compilation mode by: `xmake f -m release`. #### mode.check Add check compilation mode configuration rules to the current project's xmake.lua, generally used for memory detection, for example: ```lua add_rules("mode.check") ``` Equivalent to: ```lua if is_mode("check") then set_symbols("debug") set_optimize("none") 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 profile compilation mode configuration rules to the current project's xmake.lua, generally used for performance analysis, for example: ```lua add_rules("mode.profile") ``` Equivalent to: ```lua if is_mode("profile") then set_symbols("debug") add_cxflags("-pg") add_ldflags("-pg") end ``` We can switch to this compilation mode by: `xmake f -m profile`. #### mode.coverage Add coverage compilation mode configuration rules to the current project's xmake.lua, generally used for coverage analysis, for example: ```lua add_rules("mode.coverage") ``` Equivalent to: ```lua 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`. Note: The generated gcno files are generally in the directory corresponding to the obj, so you need to find them from the build directory. ### Extending Your Own Build Modes xmake's mode configuration doesn't have fixed values. Users can pass and configure them arbitrarily, as long as the mode value passed in `xmake f -m/--mode=xxx` can correspond to `is_mode("xxx")` in xmake.lua. For example, if we set our own unique compilation mode `my_mode`, we can directly configure and switch it on the command line: ```bash $ xmake f -m my_mode $ xmake [ 0%]: ccache compiling.my_mode src/main.cpp [100%]: linking.my_mode test build ok! ``` Then make the corresponding value judgment in xmake.lua: ```lua if is_mode("my_mode") then add_defines("ENABLE_MY_MODE") end ``` ### Using Mode Variables We can also directly pass mode variables `$(mode)` in configuration values, for example, to select different libraries to link based on different modes: ```lua target("test") set_kind("binary") add_files("src/*.c") add_links("xxx_$(mode)") ``` With the above configuration, if compiled in debug mode, it will select to link: the `libxxx_debug.a` library, while in release mode it will link `libxxx_release.a`. Of course, we can also set it in the library search path and select the corresponding library based on the directory. ```lua target("test") set_kind("binary") add_files("src/*.c") add_linkdirs("lib/$(mode)") add_links("xxx") ``` In addition, we can directly obtain the passed mode configuration value through `get_config("mode")`, and these methods of obtaining are also effective in custom scripts, for example: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load(function (target) if is_mode("release") then print(get_config("mode"), "$(mode)") end end) ``` --- --- url: /posts/quickstart-9-cross-compile.md --- xmake is a lightweight and modern c/c++ project building tool based on Lua. It's main features are: easy to use syntax, easy to use project maintenance, and a consistent build experience across platforms. In addition to built-in build support for win, linux, macOS platforms, and android, ios and other mobile platforms, xmake also supports cross-compilation support for various other toolchains. In this article, we will explain in detail how to use xmake for cross-compilation. * [Project Source](https://github.com/xmake-io/xmake) * [Official Document](https://xmake.io) ### Introduction to Cross-Compilation Toolchains Usually, if we need to compile target files that can only run on other devices in the current PC environment, we need to compile and generate them through the corresponding cross-compilation toolchain. For example, compiling Linux programs on win/macos, or compiling target files for other embedded devices on Linux, etc. Usually, cross-compilation toolchains are based on gcc/clang and mostly have a structure similar to the following: ``` /home/toolchains_sdkdir - bin - arm-linux-armeabi-gcc - arm-linux-armeabi-ld - ... - lib - libxxx.a - include - xxx.h ``` Each toolchain has corresponding include/lib directories for placing some system libraries and header files, such as libc, stdc++, etc. The bin directory contains a series of compilation toolchain tools. For example: ``` arm-linux-armeabi-ar arm-linux-armeabi-as arm-linux-armeabi-c++ arm-linux-armeabi-cpp arm-linux-armeabi-g++ arm-linux-armeabi-gcc arm-linux-armeabi-ld arm-linux-armeabi-nm arm-linux-armeabi-strip ``` The `arm-linux-armeabi-` prefix is the cross, which is used to identify the target platform and architecture, mainly to distinguish it from the host's own gcc/clang. The gcc/g++ inside are c/c++ compilers, which can usually also be used as linkers. When linking, they will internally call ld to link and automatically append some c++ libraries. cpp is the preprocessor, as is the assembler, ar is used to generate static libraries, strip is used to remove some symbol information, making the target program smaller. nm is used to view the export symbol list. ### Automatic Detection and Compilation If our cross-compilation toolchain has the structure described above, xmake will automatically detect and identify the SDK structure, extract the cross inside, and the include/lib path locations. Users usually don't need to do additional parameter settings, just configure the SDK root directory to compile, for example: ```bash $ xmake f -p cross --sdk=/home/toolchains_sdkdir $ xmake ``` Among them, `-p cross` is used to specify that the current platform is a cross-compilation platform, and `--sdk=` is used to specify the root directory of the cross toolchain. Note: We can also specify the `-p linux` platform to configure cross-compilation. The effect is the same. The only difference is that it additionally identifies the Linux platform name, which is convenient for judging the platform through `is_plat("linux")` in xmake.lua. At this time, xmake will automatically detect the prefix name cross of gcc and other compilers: `arm-linux-armeabi-`, and during compilation, it will also automatically add search options for `link libraries` and `header files`, for example: ``` -I/home/toolchains_sdkdir/include -L/home/toolchains_sdkdir/lib ``` These are all handled automatically by xmake and do not need to be configured manually. ### Manual Compilation Configuration If the automatic detection above cannot fully compile for certain toolchains, users need to manually set some cross-compilation-related configuration parameters to adjust and adapt to these special toolchains. Below I will explain how to configure them one by one. #### Setting the Toolchain bin Directory For irregular toolchain directory structures, if the simple `[--sdk](https://xmake.io)` option setting cannot fully detect and pass, you can continue to add the toolchain bin directory location through this option. For example: For some special cross toolchains, the compiler bin directory is not in the `/home/toolchains_sdkdir/bin` location, but is independently located at `/usr/opt/bin` At this time, we can add the bin directory parameter setting on the basis of setting the sdk parameter to adjust the toolchain bin directory. ```bash $ xmake f -p linux --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin $ xmake ``` #### Setting the Cross Toolchain Tool Prefix For prefixes like `aarch64-linux-android-`, usually if you configure `--sdk` or `--bin`, xmake will automatically detect it, and you don't need to set it manually. However, for some extremely special toolchains where multiple cross-prefixed tool bins are mixed together in one directory, you need to manually set this configuration to distinguish which bin to use. For example, two different compilers exist simultaneously in the toolchains bin directory: ``` /opt/bin - armv7-linux-gcc - aarch64-linux-gcc ``` If we now want to use the armv7 version, we can add the `--cross=` configuration to set the compilation tool prefix name, for example: ```bash $ xmake f -p linux --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- ``` #### Setting c/c++ Compilers If you want to further subdivide the compiler selection, continue to add related compiler options, for example: ```bash $ xmake f -p linux --sdk=/user/toolsdk --cc=armv7-linux-clang --cxx=armv7-linux-clang++ ``` Of course, we can also specify the full path of the compiler. `--cc` is used to specify the c compiler name, and `--cxx` is used to specify the c++ compiler name. Note: If CC/CXX environment variables exist, the values specified in the current environment variables will be used first. If the specified compiler name is not one of those that xmake can recognize internally (with gcc, clang, etc.), the compiler tool detection will fail. At this time, we can use: ```bash xmake f --cxx=clang++@/home/xxx/c++mips.exe ``` Set the c++mips.exe compiler to compile in a clang++-like manner. That is to say, while specifying the compiler as `c++mips.exe`, tell xmake that it is basically the same as clang++ in terms of usage and parameter options. #### Setting c/c++ Linkers If you want to further subdivide the linker selection, continue to add related linker options, for example: ```bash $ xmake f -p linux --sdk=/user/toolsdk --ld=armv7-linux-clang++ --sh=armv7-linux-clang++ --ar=armv7-linux-ar ``` ld specifies the executable program linker, sh specifies the shared library program linker, and ar specifies the archiver for generating static libraries. Note: If LD/SH/AR environment variables exist, the values specified in the current environment variables will be used first. #### Setting Header File and Library Search Directories If there are additional other include/lib directories in the SDK that are not in the standard structure, causing cross-compilation to not find libraries and header files, we can add search paths through `--includedirs` and `--linkdirs`, and then add additional link libraries through `--links`. ```bash $ xmake f -p linux --sdk=/usr/toolsdk --includedirs=/usr/toolsdk/xxx/include --linkdirs=/usr/toolsdk/xxx/lib --links=pthread ``` Note: If you want to specify multiple search directories, you can separate them with `:` or `;`, which are the path separators for different host platforms. Use `:` on linux/macos and `;` on win. #### Setting Compilation and Link Options We can also configure some compilation and link options through `--cflags`, `--cxxflags`, `--ldflags`, `--shflags`, and `--arflags` according to the actual situation. * cflags: Specify c compilation parameters * cxxflags: Specify c++ compilation parameters * cxflags: Specify c/c++ compilation parameters * asflags: Specify assembler compilation parameters * ldflags: Specify executable program link parameters * shflags: Specify dynamic library program link parameters * arflags: Specify static library generation parameters For example: ```bash $ xmake f -p linux --sdk=/usr/toolsdk --cflags="-DTEST -I/xxx/xxx" --ldflags="-lpthread" ``` ### MinGW Toolchain Using the MinGW toolchain for compilation is actually cross-compilation, but because this is commonly used, xmake has specifically added a MinGW platform to quickly handle compilation using the MinGW toolchain. Therefore, xmake's toolchain detection for MinGW will be more complete. On macOS, basically even the SDK path doesn't need to be configured, and it can be directly detected. Just switch to the MinGW platform for compilation: ```bash $ xmake f -p mingw $ xmake -v configure { ld = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ ndk_stdcxx = true plat = mingw mingw = /usr/local/opt/mingw-w64 buildir = build arch = x86_64 xcode = /Applications/Xcode.app mode = release cxx = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc cross = x86_64-w64-mingw32- theme = default kind = static ccache = true host = macosx clean = true bin = /usr/local/opt/mingw-w64/bin } [ 0%]: ccache compiling.release src/main.cpp /usr/local/bin/ccache /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc -c -fvisibility=hidden -O3 -m64 -o build/.objs/test/mingw/x86_64/release/src/main.cpp.obj src/main.cpp [100%]: linking.release test.exe /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ -o build/mingw/x86_64/release/test.exe build/.objs/test/mingw/x86_64/release/src/main.cpp.obj -s -fvisibility=hidden -m64 build ok! ``` Here we added the `-v` parameter to see the detailed compilation commands and the detected MinGW toolchain configuration values. Among them, cross was automatically detected as: `x86_64-w64-mingw32-`, the bin directory was also automatically detected, as well as the compiler and linker. Although the SDK path cannot be automatically detected on linux/win yet, we can also manually specify the SDK path. It should be noted that xmake specifically provides a `--mingw=` parameter for MinGW to specify the MinGW toolchain root directory. Its effect is the same as `--sdk=`, but it can be set as a global configuration. ```bash $ xmake g --mingw=/home/mingwsdk $ xmake f -p mingw $ xmake ``` After we set the `--mingw` root directory to the global configuration through the `xmake g/global` command, we don't need to additionally specify the MinGW toolchain path every time we compile and switch compilation platforms, which is convenient to use. In addition, the usage of other toolchain configuration parameters is no different from what was described above. Parameters like `--cross`, `--bin=` can all be controlled according to actual environmental needs to see if additional configuration is needed to adapt to your own MinGW toolchain. ### Project Description Settings #### set\_toolchain If you find it cumbersome to configure through the command line every time, some configurations can be pre-configured in xmake.lua to simplify command configuration. For example, compiler specification can be set separately for each target through `set_toolchain`. ```lua target("test") set_kind("binary") set_toolchain("cxx", "clang") set_toolchain("ld", "clang++") ``` Force the test target's compiler and linker to use the clang compiler, or specify the compiler name or path in the cross-compilation toolchain. #### set\_config We can also set the default values of each configuration parameter in the `xmake f/config` command through `set_config`. This is a global API that will take effect for each target. ```lua set_config("cflags", "-DTEST") set_config("sdk", "/home/xxx/tooksdk") set_config("cc", "gcc") set_config("ld", "g++") ``` However, we can still modify the default configuration in xmake.lua through `xmake f --name=value`. ### Custom Compilation Platforms If a cross toolchain compiles a target program that has a corresponding platform that needs to be specified, and you need to configure some additional compilation parameters in xmake.lua based on different cross-compilation platforms, then the `-p cross` setting above cannot meet the requirements. Actually, the `-p/--plat=` parameter can also be set to other custom values. You just need to maintain a corresponding relationship with `is_plat`. All non-built-in platform names will default to cross-compilation mode, for example: ```bash $ xmake f -p myplat --sdk=/usr/local/arm-xxx-gcc/ $ xmake ``` We passed in the custom platform name myplat as the compilation platform of the current cross toolchain, and then in xmake.lua we configure the corresponding settings for this platform: ```lua if is_plat("myplat") then add_defines("TEST") end ``` Through this method, xmake can easily extend to handle various compilation platforms. Users can extend support for freebsd, netbsd, sunos and other various platforms' cross-compilation themselves. I excerpted a cross-compilation configuration written when porting libuv before to give you an intuitive feel: ```lua -- for gragonfly/freebsd/netbsd/openbsd platform if is_plat("gragonfly", "freebsd", "netbsd", "openbsd") then add_files("src/unix/bsd-ifaddrs.c") add_files("src/unix/freebsd.c") add_files("src/unix/kqueue.c") add_files("src/unix/posix-hrtime.c") add_headerfiles("(include/uv-bsd.h)") end -- for sunos platform if is_plat("sunos") then add_files("src/unix/no-proctitle.c") add_files("src/unix/sunos.c") add_defines("__EXTENSIONS_", "_XOPEN_SOURCE=600") add_headerfiles("(include/uv-sunos.h)") end ``` Then, we can switch these platforms to compile: ```bash $ xmake f -p [gragonfly|freebsd|netbsd|openbsd|sunos] --sdk=/home/arm-xxx-gcc/ $ xmake ``` In addition, the built-in Linux platform also supports cross-compilation. If you don't want to configure other platform names, you can also cross-compile uniformly as a Linux platform. ```bash $ xmake f -p linux --sdk=/usr/local/arm-xxx-gcc/ $ xmake ``` As long as parameters like `--sdk=` are set, the Linux platform's cross-compilation mode will be enabled. --- --- url: /posts/xmake-update-v2.5.1.md --- ### Add add\_requireconfs to improve package configuration Despite the previous version, we can define and configure dependent packages by `add_requires("libpng", {configs = {shared = true}})`. However, if the user project has a huge project and many dependent packages, and each package requires different compilation configuration parameters, the configuration will still be very cumbersome and has limitations, such as the inability to rewrite the internal sub-dependent package configuration. Therefore, we have added `add_requireconfs` to configure the configuration of each package and its sub-dependencies more flexibly and conveniently. Below we focus on several usages: ##### Set the configuration of the specified package This is the basic usage. For example, we have declared a package through `add_requires("zlib")`, and want to expand the configuration of this zlib later and change it to dynamic library compilation. You can configure it in the following way. ```lua add_requires("zlib") add_requireconfs("zlib", {configs = {shared = true}}) ``` It is equivalent to ```lua add_requires("zlib", {configs = {shared = true}}) ``` ##### Set general default configuration The above usage, we still don't see any practical use, but if we rely on more we can see the effect, such as the following: ```lua add_requires("zlib", {configs = {shared = true}}) add_requires("pcre", {configs = {shared = true}}) add_requires("libpng", {configs = {shared = true}}) add_requires("libwebp", {configs = {shared = true}}) add_requires("libcurl", {configs = {shared = false}}) ``` Is it very cumbersome, if we use `add_requireconfs` to set the default configuration, it can be greatly simplified to the following configuration: ```lua add_requireconfs("*", {configs = {shared = true}}) add_requires("zlib") add_requires("pcre") add_requires("libpng") add_requires("libwebp") add_requires("libcurl", {configs = {shared = false}}) ``` For the above configuration, we use pattern matching through `add_requireconfs("*", {configs = {shared = true}})` to set all dependent packages to compile and install dynamic libraries by default. However, we used `add_requires("libcurl", {configs = {shared = false}})` to configure libcurl to compile and install static libraries. The final configuration result is: zlib/pcre/libpng/libwebp is a shared library, and libcurl is a static library. Through pattern matching, we can put some common configurations of each package into the unified `add_requireconfs` to pre-configure, which greatly simplifies the definition of each `add_requires`. :::NOTE By default, for the same configuration, xmake will give priority to the configuration in add\_requires instead of add\_requireconfs. ::: If the version is set in `add_requires("zlib 1.2.11")`, the configuration of add\_requires will be used first, and the version configuration in add\_requireconfs will be completely ignored. Of course, we can also completely override the version specified in `add_requires` through override . ```lua add_requires("zlib 1.2.11") add_requireconfs("zlib", {override = true, version = "1.2.10"}) ``` ##### Rewrite package dependency configuration In fact, the biggest use of `add_requireconfs` is to allow users to rewrite the configuration of specific dependent packages of the installation package. What does it mean? For example, our project integrates the package libpng and uses a dynamic library version, but the zlib library that libpng depends on is actually a static library version. ```lua add_requires("libpng", {configs = {shared = true}}) ``` So if we want to change the zlib package that libpng depends on to be compiled as a dynamic library, how should we configure it? This requires `add_requireconfs`. ```lua add_requires("libpng", {configs = {shared = true}}) add_requireconfs("libpng.zlib", {configs = {shared = true}}) ``` Through the writing method of `libpng.zlib` dependency path, specify an internal dependency and rewrite the internal dependency configuration. If the dependency path is deep, such as the dependency chain of `foo -> bar -> xyz`, we can write: `foo.bar.xyz` We can also rewrite the internal zlib library version that libpng depends on: ```lua add_requires("libpng") add_requireconfs("libpng.zlib", {version = "1.2.10"}) ``` ##### Pattern matching for cascading dependencies If a package has a lot of dependencies, and the dependency level is also very deep, what to do, for example, the package libwebp, its dependencies are: ``` libwebp - libpng - zlib - cmake - libjpeg - libtiff - zlib - giflib - cmake ``` If I want to rewrite all the dependent libraries in libwebp to add specific configuration, then the configuration one by one will be very cumbersome. At this time, the recursive dependency pattern matching of `add_requireconfs()` is needed to support. ```lua add_requires("libwebp") add_requireconfs("libwebp.**|cmake", {configs = {cxflags = "-DTEST"}}) ``` In the above configuration, we added `-DTEST` to compile all the library dependencies in libwebp, but the cmake dependency is a build tool dependency, and we can exclude it by way of `|xxx`. The pattern matching here is very similar to `add_files()`. We are giving a few examples. For example, this time we only rewrite the single-level dependency configuration under libwebp to enable the debugging library: ```lua add_requires("libwebp") add_requireconfs("libwebp.*|cmake", {debug = true}) ``` ### Graphical configuration supports mouse and scroll operations We upgraded the tui component library used by xmake: [LTUI](https://github.com/tboox/ltui), added support for the mouse, and scrolling support for some components, we can go to the graphical configuration, More flexible and convenient configuration of compilation options. ### stdin redirect support In the previous version, the process execution interface such as os.execv/os.runv provided by xmake only supports stdout/stderr output redirection, but does not support stdin input redirection, so in this version, we also do it Supported. The usage is as follows: ```lua os.execv("foo", {"arg1", "arg2"}, {stdin = "/tmp/a"}) ``` When we execute the process, we can use the /tmp/a file as the redirected input. Of course, we can also pass `{stdout = "/tmp/out"}` as the redirected output. ### vs project grouping support We have added a new interface `set_group` to support grouping of each target. This interface is currently only used for vs/vsxmake project generation. The subproject directory tree within the vs project is displayed in groups according to the specified structure, but it may also be Other modules increase grouping support. For example, for the following grouping configuration: ```lua add_rules("mode.debug", "mode.release") target("test1") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test2") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test3") set_kind("binary") add_files("src/*.cpp") set_group("group1/group2") target("test4") set_kind("binary") add_files("src/*.cpp") set_group("group3/group4") target("test5") set_kind("binary") add_files("src/*.cpp") target("test6") set_kind("binary") add_files("src/*.cpp") ``` The effect of the generated VS project directory structure is as follows: ![](/assets/img/manual/set_group.png) Among them, `set_group("group1/group2")` can set the target to the secondary group. ### Automatically update vs project If you feel that it is cumbersome to generate and update the VS project through the `xmake project -k vsxmake` command every time, we can now configure the `plugin.vsxmake.autoupdate` rule in xmake.lua to achieve automatic update. Users can automatically update the VS project if there are changes to the file list or xmake.lua after each execution of the build in the VS project. ```lua add_rules("plugin.vsxmake.autoupdate") target("test") set_kind("binary") add_files("src/*.c") ``` ### Improve vs/vsxmake project plugin In addition to the group support and automatic updates mentioned above, in this version, we also fixed a lot of VS project-related issues, such as: intellisense prompt improvement, path truncation problem repair, full support for remote dependency packages ### Improve windows registry support xmake improves the internal winos module and adds some interfaces to access the registry more conveniently and obtain the registry configuration on windows. #### winos.registry\_keys * Get the list of registry builds Support through pattern matching, traverse to obtain the registry key path list, `*` is single-level path matching, `**` is recursive path matching. ```lua local keypaths = winos.registry_keys("HKEY_LOCAL_MACHINE\\SOFTWARE\\*\\Windows NT\\*\\CurrentVersion\\AeDebug") for _, keypath in ipairs(keypaths) do print(winos.registry_query(keypath .. ";Debugger")) end ``` #### winos.registry\_values * Get a list of registry value names Support to obtain the value name list of the specified key path through pattern matching, and the string after the `;` is the specified key name pattern matching string. ```lua local valuepaths = winos.registry_values("HKEY_LOCAL_MACHINE\\SOFTWARE\\xx\\AeDebug;Debug*") for _, valuepath in ipairs(valuepaths) do print(winos.registry_query(valuepath)) end ``` #### winos.registry\_query * Get the registry value Get the value under the specified registry path, if the value name is not specified, then get the default value of the key path ```lua local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug") local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger") ``` ### Support for building Zig project In the last version, xmake has experimentally supported zig, but there were also many pitfalls during the period, especially when building on windows/macos encountered many problems. Then in the latest zig 0.7.1, most of the problems I encountered have been fixed, and now xmake can already support zig project compilation. We can quickly create a Zig empty project with the following command: ```bash $ xmake create -l zig console ``` The content of xmake.lua is as follows: ```lua add_rules("mode.debug", "mode.release") target("console") set_kind("binary") add_files("src/*.zig") ``` As you can see, the configuration method is actually no different from C/C++. Because Zig and C have good binary compatibility, we can also use `add_requires` to add remote dependency support for C/C++ packages to the zig project. Then execute xmake to complete the compilation. ```bash $ xmake ``` Then continue to run the run command, you can directly execute the zig program and output the running result. ```bash $ xmake run Hello world! ``` We can also easily implement the mixed compilation support of C and Zig, just add the corresponding C code file. ```lua add_rules("mode.debug", "mode.release") target("console") set_kind("binary") add_files("src/*.zig", "src/*.c") ``` For complete code examples, see: [Zig with C](https://github.com/xmake-io/xmake/blob/dev/tests/projects/zig/console_c_call_zig/xmake.lua) ### Luarocks plugin [luarocks](https://luarocks.org/) is a package management tool of lua, which provides the installation and integration of various lua modules, but it uses a built-in construction mechanism to build lua c modules. For example, in its rockspec file, the builtin build type is used to describe the construction of common lua c modules: ```lua build = { type = "builtin", modules = { ["module.hello"] = { sources = "src/test.c" } }, copy_directories = {} } ``` This is not a problem for small modules, but if the c code structure of the module is more complicated, its built-in construction rules still have many limitations and are not flexible. In addition, switching msvc / mingw tool chain and parameter configuration etc. Neither is flexible enough. Therefore, xmake provides [luarocks=build-xmake](https://github.com/xmake-io/luarocks-build-xmake) plugin to use xmake to replace the built-in build system of luarocks. The replacement method is also very simple. You only need to buildin Change the build type to xmake and add the luarocks-build-xmake dependency. ```lua dependencies = { "lua >= 5.1", "luarocks-build-xmake" } build = { type = "xmake", modules = { ["module.hello"] = { sources = "src/test.c" } }, copy_directories = {} } ``` But this is still very cumbersome. It is still necessary to describe the rules based on the source file list in the modules in the rockspec file, and then luarocks-build-xmake will automatically generate xmake.lua according to the configuration to complete the build. But since xmake is used, your own lua module can be maintained with xmake.lua, so the build configuration is more flexible, so we only need the following. ```lua dependencies = { "lua >= 5.1", "luarocks-build-xmake" } build = { type = "xmake", copy_directories = {} } ``` You only need to set the current switch to xmake compilation, and use the xmake.lua rules file built into the lua module project. ### Support for deploying Qt programs on windows Thank you very much for the contribution of @SirLynix, xmake can already support the deployment and installation of Qt applications on windows. We only need to maintain a Qt program normally, for example: ```lua add_rules("mode.debug", "mode.release") target("demo") add_rules("qt.quickapp") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/qml.qrc") ``` Then, we only need to execute the following compile and install commands, and xmake will automatically call the windeployqt.exe program to install and deploy our Qt application. ```bash $ xmake $ xmake install -o d:\installdir ``` Related patches: [#1145](https://github.com/xmake-io/xmake/pull/1145) In addition, in the previous version, xmake has also supported the deployment and packaging of Qt programs for macOS and android versions. Each time only normal compilation commands are required, the QT .app/.apk installation package can be generated. ```bash $ xmake f -p android --ndk=/xxx/android-ndk-r20b --sdk=/xxx $ xmake ``` ### Some bug fixes We have also fixed many problems reported by users. Here we introduce some more important bug fixes, such as: We fixed the problem of empty double quotes in `add_defines("TEST=\"hello world\"")`, which caused errors in previous compilation. In addition, we improved the search and support of the vstudio environment, and solved the problem of compilation failure caused by Chinese in the user's home directory and environment variables. We have also improved the llvm toolchain to solve the problem of the lack of isysroot configuration when using the llvm tool chain under macOS if xcode is not installed, and the occasional failure of the header file dependency compilation under msvc. ## Changelog ### New features * [#1035](https://github.com/xmake-io/xmake/issues/1035): The graphics configuration menu fully supports mouse events, and support scroll bar * [#1098](https://github.com/xmake-io/xmake/issues/1098): Support stdin for os.execv * [#1079](https://github.com/xmake-io/xmake/issues/1079): Add autoupdate plugin rule for vsxmake, `add_rules("plugin.vsxmake.autoupdate")` * Add `xmake f --vs_runtime=MT` and `set_runtimes("MT")` to set vs runtime for targets and packages * [#1032](https://github.com/xmake-io/xmake/issues/1032): Support to enum registry keys and values * [#1026](https://github.com/xmake-io/xmake/issues/1026): Support group for vs/vsxmake project * [#1178](https://github.com/xmake-io/xmake/issues/1178): Add `add_requireconfs()` api to rewrite configs of depend packages * [#1043](https://github.com/xmake-io/xmake/issues/1043): Add `luarocks.module` rule for luarocks-build-xmake * [#1190](https://github.com/xmake-io/xmake/issues/1190): Support for Apple Silicon (macOS ARM) * [#1145](https://github.com/xmake-io/xmake/pull/1145): Support Qt deploy for Windows, thanks @SirLynix ### Change * [#1072](https://github.com/xmake-io/xmake/issues/1072): Fix and improve to parse cl deps * Support utf8 for ui modules and `xmake f --menu` * Improve to support zig on macOS * [#1135](https://github.com/xmake-io/xmake/issues/1135): Improve multi-toolchain and multi-platforms for targets * [#1153](https://github.com/xmake-io/xmake/issues/1153): Improve llvm toolchain to support sysroot on macOS * [#1071](https://github.com/xmake-io/xmake/issues/1071): Improve to generate vs/vsxmake project to support for remote packages * Improve vs/vsxmake project plugin to support global `set_arch()` setting * [#1164](https://github.com/xmake-io/xmake/issues/1164): Improve to launch console programs for vsxmake project * [#1179](https://github.com/xmake-io/xmake/issues/1179): Improve llvm toolchain and add isysroot ### Bugs fixed * [#1091](https://github.com/xmake-io/xmake/issues/1091): Fix incorrect ordering of inherited library dependencies * [#1105](https://github.com/xmake-io/xmake/issues/1105): Fix c++ language intellisense for vsxmake * [#1132](https://github.com/xmake-io/xmake/issues/1132): Fix TrimEnd bug for vsxmake * [#1142](https://github.com/xmake-io/xmake/issues/1142): Fix git not found when installing packages * Fix macos.version bug for macOS Big Sur * [#1084](https://github.com/xmake-io/xmake/issues/1084): Fix `add_defines()` bug (contain spaces) * [#1195](https://github.com/xmake-io/xmake/pull/1195): Fix unicode problem for vs and improve find\_vstudio/os.exec --- --- url: /zh/posts/xmake-update-v2.5.1.md --- 这是 xmake 在今年的首个版本,也是完全适配支持 Apple Silicon (macOS ARM) 设备的首个版本。 这个版本,我们主要改进了对 C/C++ 依赖包的集成支持,更加的稳定,并且能够更加灵活的实现定制化配置编译。 另外,我们还重点改进 vs/vsxmake 两个vs工程生成器插件,修复了很多细节问题,并且对子工程`分组`也做了支持,现在可以生成类似下图的工程结构。 ![](/assets/img/manual/set_group.png) 关于 Zig 方面,0.7.1 版本修复了很多我之前反馈的问题,现在 xmake 也已经可以很好的支持对 zig 项目的编译。 同时,我们还新开发了一个 [luarocks=build-xmake](https://github.com/xmake-io/luarocks-build-xmake) 插件去用 xmake 替换 luarocks 内置的构建系统。 最后,在这个版本中,我们继续改进了 `xmake f --menu` 图形化配置菜单,完全支持鼠标操作和滚动支持,也对 utf8 做了支持。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) * [入门课程](https://xmake.io/zh/) ## 新特性介绍 ### 新增 add\_requireconfs 改进包配置 尽管之前的版本,我们可以通过 `add_requires("libpng", {configs = {shared = true}})` 的方式来定义和配置依赖包。 但是,如果用户项目的工程庞大,依赖包非常多,且每个包都需要不同的编译配置参数,那么配置起来还是会非常繁琐,并且具有局限性,比如无法改写内部的子依赖包配置。 因此,我们新增了 `add_requireconfs` 去更灵活方便的配置每个包的配置以及它的子依赖,下面我们重点介绍几种用法: ##### 扩充指定包的配置 这是基本用法,比如我们已经通过 `add_requires("zlib")` 声明了一个包,想要在后面对这个 zlib 的配置进行扩展,改成动态库编译,可以通过下面的方式配置。 ```lua add_requires("zlib") add_requireconfs("zlib", {configs = {shared = true}}) ``` 它等价于 ```lua add_requires("zlib", {configs = {shared = true}}) ``` ##### 设置通用的默认配置 上面的用法,我们还看不出有什么实际用处,但如果依赖多了就能看出效果了,比如下面这样: ```lua add_requires("zlib", {configs = {shared = true}}) add_requires("pcre", {configs = {shared = true}}) add_requires("libpng", {configs = {shared = true}}) add_requires("libwebp", {configs = {shared = true}}) add_requires("libcurl", {configs = {shared = false}}) ``` 是不是非常繁琐,如果我们用上 `add_requireconfs` 来设置默认配置,就可以极大的简化成下面的配置: ```lua add_requireconfs("*", {configs = {shared = true}}) add_requires("zlib") add_requires("pcre") add_requires("libpng") add_requires("libwebp") add_requires("libcurl", {configs = {shared = false}}) ``` 上面的配置,我们通过 `add_requireconfs("*", {configs = {shared = true}})` 使用模式匹配的方式,设置所有的依赖包默认走动态库编译安装。 但是,我们又通过 `add_requires("libcurl", {configs = {shared = false}})` 将 libcurl 进行了特殊配置,强制走静态库编译安装。 最终的配置结果为:zlib/pcre/libpng/libwebp 是 shared 库,libcurl 是静态库。 我们通过模式匹配的方式,可以将一些每个包的常用配置都放置到统一的 `add_requireconfs` 中去预先配置好,极大简化每个 `add_requires` 的定义。 :::注意 默认情况下,对于相同的配置,xmake 会优先使用 add\_requires 中的配置,而不是 add\_requireconfs。 ::: 如果 `add_requires("zlib 1.2.11")` 中设置了版本,就会优先使用 add\_requires 的配置,完全忽略 add\_requireconfs 里面的版本配置,当然我们也可以通过 override 来完全重写 `add_requires` 中指定的版本。 ```lua add_requires("zlib 1.2.11") add_requireconfs("zlib", {override = true, version = "1.2.10"}) ``` ##### 改写包依赖配置 其实 `add_requireconfs` 最大的用处是可以让用户改写安装包的特定依赖包的配置。 什么意思呢,比如我们项目中集成使用 libpng 这个包,并且使用了动态库版本,但是 libpng 内部依赖的 zlib 库其实还是静态库版本。 ```lua add_requires("libpng", {configs = {shared = true}}) ``` 那如果我们想让 libpng 依赖的 zlib 包也改成动态库编译,应该怎么配置呢?这就需要 `add_requireconfs` 了。 ```lua add_requires("libpng", {configs = {shared = true}}) add_requireconfs("libpng.zlib", {configs = {shared = true}}) ``` 通过 `libpng.zlib` 依赖路径的写法,指定内部某个依赖,改写内部依赖配置。 如果依赖路径很深,比如 `foo -> bar -> xyz` 的依赖链,我们可以写成:`foo.bar.xyz` 我们也可以改写 libpng 依赖的内部 zlib 库版本: ```lua add_requires("libpng") add_requireconfs("libpng.zlib", {version = "1.2.10"}) ``` ##### 级联依赖的模式匹配 如果一个包的依赖非常多,且依赖层次也很深,怎么办呢,比如 libwebp 这个包,它的依赖有: ``` libwebp - libpng - zlib - cmake - libjpeg - libtiff - zlib - giflib - cmake ``` 如果我想改写 libwebp 里面的所有的依赖库都加上特定配置,那么挨个配置,就会非常繁琐,这个时候就需要 `add_requireconfs()` 的递归依赖模式匹配来支持了。 ```lua add_requires("libwebp") add_requireconfs("libwebp.**|cmake", {configs = {cxflags = "-DTEST"}}) ``` 上面的配置,我们将 libwebp 中所以的库依赖就额外加上了 `-DTEST` 来编译,但是 cmake 依赖属于构建工具依赖,我们可以通过 `|xxx` 的方式排除它。 这里的模式匹配写法,与 `add_files()` 非常类似。 我们在给几个例子,比如这回我们只改写 libwebp 下单级的依赖配置,启用调试库: ```lua add_requires("libwebp") add_requireconfs("libwebp.*|cmake", {debug = true}) ``` ### 图形化配置支持鼠标和滚动操作 我们升级了 xmake 所使用的 tui 组件库:[LTUI](https://github.com/tboox/ltui),增加了对鼠标的支持,以及部分组件的滚动支持,我们可以再图形化配置中,更加灵活方便的配置编译选项。 ### stdin 重定向输入支持 之前的版本中,xmake 提供的 os.execv/os.runv 等进程执行接口,仅仅只支持 stdout/stderr 输出重定向,但是并不支持 stdin 输入重定向,因此在这个版本中,我们对其也做了支持。 使用方式如下: ```lua os.execv("foo", {"arg1", "arg2"}, {stdin = "/tmp/a"}) ``` 我们可以执行进程的时候,将 /tmp/a 文件作为重定向输入,当然我们还可以传递 `{stdout = "/tmp/out"}` 等作为重定向输出。 ### vs 工程分组支持 我们新增了一个接口 `set_group`,来对每个 target 进行分组支持,此接口目前仅用于 vs/vsxmake 工程生成,对 vs 工程内部子工程目录树按指定结构分组展示,不过后续也可能对其他模块增加分组支持。 比如对于下面的分组配置: ```lua add_rules("mode.debug", "mode.release") target("test1") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test2") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test3") set_kind("binary") add_files("src/*.cpp") set_group("group1/group2") target("test4") set_kind("binary") add_files("src/*.cpp") set_group("group3/group4") target("test5") set_kind("binary") add_files("src/*.cpp") target("test6") set_kind("binary") add_files("src/*.cpp") ``` 生成的 vs 工程目录结构效果如下: ![](/assets/img/manual/set_group.png) 其中 `set_group("group1/group2")` 可以将 target 设置到二级分组中去。 ### vs 工程自动更新规则 如果觉得每次通过 `xmake project -k vsxmake` 命令来生成和更新 vs 工程很繁琐,我们现在可以通过在 xmake.lua 中配置 `plugin.vsxmake.autoupdate` 规则来实现自动更新。 用户可以在 vs 工程中每次执行构建后,如果文件列表或者 xmake.lua 有改动,vs 工程都会自动更新。 ```lua add_rules("plugin.vsxmake.autoupdate") target("test") set_kind("binary") add_files("src/*.c") ``` ### vs/vsxmake 工程插件改进 除了上面提到的分组支持和自动更新,这个版本中,我们还修复了不少 vs 工程相关的问题,比如:intellisense 提示改进,路径被截断的问题修复,全面支持远程依赖包 ### 改进 windows 注册表支持 xmake 改进了内部的 winos 模块,新增了一些接口来更加方便的访问注册表,获取 windows 上的注册表配置。 #### winos.registry\_keys * 获取注册表建列表 支持通过模式匹配的方式,遍历获取注册表键路径列表,`*` 为单级路径匹配,`**` 为递归路径匹配。 ```lua local keypaths = winos.registry_keys("HKEY_LOCAL_MACHINE\\SOFTWARE\\*\\Windows NT\\*\\CurrentVersion\\AeDebug") for _, keypath in ipairs(keypaths) do print(winos.registry_query(keypath .. ";Debugger")) end ``` #### winos.registry\_values * 获取注册表值名列表 支持通过模式匹配的方式,获取指定键路径的值名列表,`;` 之后的就是指定的键名模式匹配字符串。 ```lua local valuepaths = winos.registry_values("HKEY_LOCAL_MACHINE\\SOFTWARE\\xx\\AeDebug;Debug*") for _, valuepath in ipairs(valuepaths) do print(winos.registry_query(valuepath)) end ``` #### winos.registry\_query * 获取注册表建值 获取指定注册表建路径下的值,如果没有指定值名,那么获取键路径默认值 ```lua local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug") local value, errors = winos.registry_query("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug;Debugger") ``` ### Zig 项目构建支持 在上个版本中,xmake 已经对 zig 进行了实验性支持,但是期间也躺了不少坑,尤其是 windows/macos 上构建中遇到了不少问题。 然后在最新的 zig 0.7.1 中,已经将我遇到的大部分问题都修复了,现在 xmake 已经可以很好的支持 zig 项目编译。 我们可以通过下面的命令,快速创建一个 Zig 空工程: ```bash $ xmake create -l zig console ``` xmake.lua 内容如下: ```lua add_rules("mode.debug", "mode.release") target("console") set_kind("binary") add_files("src/*.zig") ``` 可以看到,其实配置方式跟 C/C++ 并没有什么不同,由于 Zig 和 C 有很好的二进制兼容,因此我们也可以使用 `add_requires` 来给 zig 项目添加 C/C++ 包的远程依赖支持。 然后执行 xmake 就可以完成编译了。 ```bash $ xmake ``` 然后继续运行 run 命令,就可以直接执行 zig 程序,输出运行结果。 ```bash $ xmake run Hello world! ``` 我们还可以很方便的实现 C 和 Zig 的混合编译支持,只需要添加上对应的 C 代码文件就可以了。 ```lua add_rules("mode.debug", "mode.release") target("console") set_kind("binary") add_files("src/*.zig", "src/*.c") ``` 完整代码例子见:[Zig with C](https://github.com/xmake-io/xmake/blob/dev/tests/projects/zig/console_c_call_zig/xmake.lua) ### Luarocks 插件 [luarocks](https://luarocks.org/) 是 lua 的一个包管理工具,提供了各种 lua 模块的安装集成,不过它本身在对 lua c 模块进行构建是采用的内建的构建机制。 比如在它的 rockspec 文件中通过 builtin 构建类型来描述常用 lua c 模块的构建: ```lua build = { type = "builtin", modules = { ["module.hello"] = { sources = "src/test.c" } }, copy_directories = {} } ``` 这对于小模块而言,并没有什么问题,但如果模块的 c 代码结构比较复杂,它内置的构建规则还是有很多的局限性,并不灵活,另外切换 msvc / mingw 工具链以及参数配置什么的都不够灵活。 因此,xmake 提供了 [luarocks=build-xmake](https://github.com/xmake-io/luarocks-build-xmake) 插件去替换 luarocks 内置的构建系统,替换方式也很简单,只需要将 builtin 构建类型改成 xmake,并加上 luarocks-build-xmake 依赖就行了。 ```lua dependencies = { "lua >= 5.1", "luarocks-build-xmake" } build = { type = "xmake", modules = { ["module.hello"] = { sources = "src/test.c" } }, copy_directories = {} } ``` 但是这样还是很繁琐,还是要基于 rockspec 文件中 modules 中的源文件列表来描述规则,然后 luarocks-build-xmake 会自动根据配置生成 xmake.lua 来完成构建。 不过既然用了 xmake,那么自己的 lua 模块,完全可以用 xmake.lua 来维护,这样构建配置就更加灵活了,因此我们只需要下面这样就行了。 ```lua dependencies = { "lua >= 5.1", "luarocks-build-xmake" } build = { type = "xmake", copy_directories = {} } ``` 只需要设置当前切换到 xmake 编译,完全使用 lua 模块项目内置的 xmake.lua 规则文件。 ### 支持在 windows 安装部署 Qt 程序 非常感谢 @SirLynix 的贡献,xmake 已经可以支持在 windows 上部署安装 Qt 应用程序。 我们只需要正常维护一个 Qt 程序,例如: ```lua add_rules("mode.debug", "mode.release") target("demo") add_rules("qt.quickapp") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/qml.qrc") ``` 然后,我们只需要执行下面的编译安装命令,xmake 就会自动调用 windeployqt.exe 程序去安装部署我们的 Qt 应用。 ```bash $ xmake $ xmake install -o d:\installdir ``` 相关补丁:[#1145](https://github.com/xmake-io/xmake/pull/1145) 另外,在之前的版本中,xmake 也已经支持对 macOS 和 android 版本的 Qt 程序进行部署打包,每次只需要正常的编译命令,就可以生成 QT .app/.apk 安装包。 ```bash $ xmake f -p android --ndk=/xxx/android-ndk-r20b --sdk=/xxx $ xmake ``` ### 一些问题修复 我们还修复了不少用户反馈的问题,这里我们介绍一些比较重要的 bug 修复,例如: 我们修复了 `add_defines("TEST=\"hello world\"")` 中内部带有空的双引号问题,之前编译会出错。 另外我们改进了 vstudio 环境的查找和支持,解决了用户 home 目录和环境变量中带有中文导致的编译失败问题。 我们也改进了 llvm 工具链,解决了 macOS 下如果没有安装 xcode 的情况下,使用 llvm 工具链缺少 isysroot 配置问题,以及 msvc 下头文件依赖编译偶尔失效问题。 ## 更新内容 ### 新特性 * [#1035](https://github.com/xmake-io/xmake/issues/1035): 图形配置菜单完整支持鼠标事件,并且新增滚动栏 * [#1098](https://github.com/xmake-io/xmake/issues/1098): 支持传递 stdin 到 os.execv 进行输入重定向 * [#1079](https://github.com/xmake-io/xmake/issues/1079): 为 vsxmake 插件添加工程自动更新插件,`add_rules("plugin.vsxmake.autoupdate")` * 添加 `xmake f --vs_runtime=MT` 和 `set_runtimes("MT")` 去更方便的对 target 和 package 进行设置 * [#1032](https://github.com/xmake-io/xmake/issues/1032): 支持枚举注册表 keys 和 values * [#1026](https://github.com/xmake-io/xmake/issues/1026): 支持对 vs/vsmake 工程增加分组设置 * [#1178](https://github.com/xmake-io/xmake/issues/1178): 添加 `add_requireconfs()` 接口去重写依赖包的配置 * [#1043](https://github.com/xmake-io/xmake/issues/1043): 为 luarocks 模块添加 `luarocks.module` 构建规则 * [#1190](https://github.com/xmake-io/xmake/issues/1190): 添加对 Apple Silicon (macOS ARM) 设备的支持 * [#1145](https://github.com/xmake-io/xmake/pull/1145): 支持在 windows 上安装部署 Qt 程序, 感谢 @SirLynix ### 改进 * [#1072](https://github.com/xmake-io/xmake/issues/1072): 修复并改进 cl 编译器头文件依赖信息 * 针对 ui 模块和 `xmake f --menu` 增加 utf8 支持 * 改进 zig 语言在 macOS 上的支持 * [#1135](https://github.com/xmake-io/xmake/issues/1135): 针对特定 target 改进多平台多工具链同时配置支持 * [#1153](https://github.com/xmake-io/xmake/issues/1153): 改进 llvm 工具链,针对 macos 上编译增加 isysroot 支持 * [#1071](https://github.com/xmake-io/xmake/issues/1071): 改进 vs/vsxmake 生成插件去支持远程依赖包 * 改进 vs/vsxmake 工程生成插件去支持全局的 `set_arch()` 设置 * [#1164](https://github.com/xmake-io/xmake/issues/1164): 改进 vsxmake 插件调试加载 console 程序 * [#1179](https://github.com/xmake-io/xmake/issues/1179): 改进 llvm 工具链,添加 isysroot ### Bugs 修复 * [#1091](https://github.com/xmake-io/xmake/issues/1091): 修复不正确的继承链接依赖 * [#1105](https://github.com/xmake-io/xmake/issues/1105): 修复 vsxmake 插件 c++ 语言标准智能提示错误 * [#1132](https://github.com/xmake-io/xmake/issues/1132): 修复 vsxmake 插件中配置路径被截断问题 * [#1142](https://github.com/xmake-io/xmake/issues/1142): 修复安装包的时候,出现git找不到问题 * 修复在 macOS Big Sur 上 macos.version 问题 * [#1084](https://github.com/xmake-io/xmake/issues/1084): 修复 `add_defines()` 中带有双引号和空格导致无法正确处理宏定义的问题 * [#1195](https://github.com/xmake-io/xmake/pull/1195): 修复 unicode 编码问题,改进 vs 环境查找和进程执行 --- --- url: /posts/xmake-update-v2.5.2.md --- ### Automatically pull the remote cross-compilation toolchain Starting from version 2.5.2, we can pull the specified toolchain to integrate the compilation project, and we also support switching the dependent package to the corresponding remote toolchain to participate in the compilation and integration. For related example codes, see: [Toolchain/Packages Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/package) Related issue [#1217](https://github.com/xmake-io/xmake/issues/1217) Currently, we have included the following toolchain packages in the [xmake-repo](https://github.com/xmake-io/xmake-repo) repository, allowing xmake to pull and integrate remotely: * llvm * llvm-mingw * gnu-rm * muslcc * zig Although there are not many toolchain packages currently supported, but the overall architecture has been opened up, we only need to include more toolchains in the later stage, such as: gcc, tinyc, vs-buildtools and other toolchains. Since the xmake package supports semantic versions, if the project relies on a specific version of the gcc/clang compiler, users should not bother to install it. xmake will automatically detect whether the gcc/clang version of the current system meets the requirements. If the version is not satisfied, xmake will pull it remotely, automatically install and integrate a specific version of gcc/clang, and then compile the project. #### Pull the specified version of llvm toolchain We use clang in llvm-10 to compile the project. ```lua add_requires("llvm 10.x", {alias = "llvm-10"}) target("test") set_kind("binary") add_files("src/*.c) set_toolchains("llvm@llvm-10") ``` Among them, the first half of `llvm@llvm-10` is the toolchain name, which is `toolchain("llvm")`, and the following name is the name of the toolchain package that needs to be associated, which is `package("llvm")` , But if an alias is set, the alias will be used first: `llvm-10` In addition, we will add the gcc toolchain package to xmake-repo in the future, so that users can freely switch to gcc-10, gcc-11 and other specific versions of gcc compilers without the need for users to manually install them. ### Pull the cross-compilation toolchain We can also pull the specified cross-compilation toolchain to compile the project. ```lua add_requires("muslcc") target("test") set_kind("binary") add_files("src/*.c) set_toolchains("@muslcc") ``` muslcc is a cross-compilation toolchain provided by https://musl.cc. By default, xmake will automatically integrate and compile the `x86_64-linux-musl-` target platform. Of course, we can also use `xmake f -a arm64` to switch to the `aarch64-linux-musl-` target platform for cross-compilation. #### Pull the toolchain and integrate the dependency packages We can also use the specified muslcc cross-compilation toolchain to compile and integrate all dependent packages. ```lua add_requires("muslcc") add_requires("zlib", "libogg", {system = false}) set_toolchains("@muslcc") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libogg") ``` At this time, the zlib, libogg and other dependent packages configured in the project will also switch to the muslcc toolchain, automatically download, compile and integrate them. We can also use `set_plat/set_arch` to fix the platform, so that only one xmake command is needed to complete the integration of the entire cross-compilation environment and architecture switching. ```lua add_requires("muslcc") add_requires("zlib", "libogg", {system = false}) set_plat("cross") set_arch("arm64") set_toolchains("@muslcc") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libogg") ``` For complete examples, see: [Examples (muslcc)](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/toolchain_muslcc/xmake.lua) #### Pull Zig toolchain xmake will first download a specific version of the zig toolchain, and then use this toolchain to compile the zig project. Of course, if the user has installed the zig toolchain by himself, xmake will also automatically detect whether the corresponding version is satisfied, and if it meets the requirements, it will use it directly , No need to download and install repeatedly. ```lua add_rules("mode.debug", "mode.release") add_requires("zig 0.7.x") target("test") set_kind("binary") add_files("src/*.zig") set_toolchains("@zig") ``` ### Support for zig cc compiler `zig cc` is the built-in c/c++ compiler of zig, which can compile and link c/c++ code completely independently. It does not rely on gcc/clang/msvc at all, which is very powerful. Therefore, we can use it to compile c/c++ projects. The key is that the zig toolchain is still very lightweight, only tens of M. We only need to switch to the zig toolchain to complete the compilation: ```bash $ xmake f --toolchain=zig $ xmake [25%]: compiling.release src/main.c "zig cc" -c -arch x86_64 -fvisibility=hidden -O3 -DNDEBUG -o build/.objs/xmake_test/macosx/x86_64/release/src/main.c.o src/main.c [50%]: linking.release test "zig c++" -o build/macosx/x86_64/release/test build/.objs/xmake_test/macosx/x86_64/release/src/main.c.o -arch x86_64 -stdlib=libc++ -Wl,-x -lz [100%]: build ok! ``` In addition, another powerful feature of `zig cc` is that it also supports cross-compilation of different architectures, which is so happy. With xmake, we only need to switch the architecture to arm64 to achieve cross-compilation of arm64, for example: ```bash $ xmake f -a arm64 --toolchain=zig $ xmake [25%]: compiling.release src/main.c "zig cc" -c -target aarch64-macos-gnu -arch arm64 -fvisibility=hidden -O3 -DNDEBUG -o build/.objs/xmake_test/macosx/arm64/release/src/main.c.o src/main.c checking for flags (-MMD -MF) ... ok checking for flags (-fdiagnostics-color=always) ... ok [50%]: linking.release xmake_test "zig c++" -o build/macosx/arm64/release/xmake_test build/.objs/xmake_test/macosx/arm64/release/src/main.co -target aarch64-macos-gnu -arch arm64 -stdlib=libc++ -Wl, -x -lz [100%]: build ok! ``` Even if you are on macOS, you can use `zig cc` to cross-compile windows/x64 target programs, which is equivalent to replacing what mingw does. ```bash $ xmake f -p windows -a x64 --toolchain=zig $ xmake ``` ### Automatically export all symbols in windows/dll There is such a function in cmake: `WINDOWS_EXPORT_ALL_SYMBOLS`, the statement in the installation cmake document: > Enable this boolean property to automatically create a module definition (.def) file with all global symbols found > in the input .obj files for a SHARED library (or executable with ENABLE\_EXPORTS) on Windows. > The module definition file will be passed to the linker causing all symbols to be exported from the .dll. For global data symbols, > \_\_declspec(dllimport) must still be used when compiling against the code in the .dll. All other function symbols will be automatically exported and imported by callers. > This simplifies porting projects to Windows by reducing the need for explicit dllexport markup, even in C++ classes. Now, xmake also provides a similar feature, which can quickly export symbols in windows/dll in full to simplify the process of symbol export in the process of porting to third-party projects. In addition, if there are too many symbols in the project, you can also use this to simplify the explicit export requirements in the code. We only need to configure the `utils.symbols.export_all` rule on the corresponding generated dll target. ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_all") target("test") set_kind("binary") add_deps("foo") add_files("src/main.c") ``` xmake will automatically scan all obj object files, then generate the def symbol export file, and pass it to link.exe for fast and full export. ### Convert mingw/.dll.a to msvc/.lib This feature is also based on the CMAKE\_GNUtoMS function, which can convert the dynamic library (xxx.dll & xxx.dll.a) generated by MinGW into a format (xxx.dll & xxx.lib) that can be recognized by Visual Studio to achieve mixed compilation. This feature is particularly helpful for Fortran & C++ mixed projects, because VS does not provide the fortran compiler, you can only use MinGW's gfortran to compile the fortran part, and then link with the VS project. Often such projects also have some other libraries provided in the vs format, so it is not possible to compile it purely with MinGW. You can only use this function of cmake to mix and compile. Therefore, xmake also provides an auxiliary module interface to support it, and the usage is as follows: ```lua import("utils.platform.gnu2mslib") gnu2mslib("xxx.lib", "xxx.dll.a") gnu2mslib("xxx.lib", "xxx.def") gnu2mslib("xxx.lib", "xxx.dll.a", {dllname = "xxx.dll", arch = "x64"}) ``` Support to generate xxx.lib from def, and also support to automatically export .def from xxx.dll.a, and then generate xxx.lib For details, see: [issue #1181](https://github.com/xmake-io/xmake/issues/1181) ### Add batch commands to simplify custom rules In order to simplify the configuration of user-defined rules, xmake newly provides custom script entries such as `on_buildcmd_file`, `on_buildcmd_files`, etc. We can construct a batch command line task through the batchcmds object, and xmake executes these commands at one time when actually executing the build. This is very useful for project generator plugins such as `xmake project`, because third-party project files generated by the generator do not support the execution of built-in scripts such as `on_build_files`. But the final result of the `on_buildcmd_file` construction is a batch of original cmd command lines, which can be directly executed as custom commands for other project files. In addition, compared to `on_build_file`, it also simplifies the implementation of compiling extension files, is more readable and easy to configure, and is more user-friendly. ```lua rule("foo") set_extensions(".xxx") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}) end) ``` In addition to `batchcmds:vrunv`, we also support some other batch commands, such as: ```lua batchcmds:show("hello %s", "xmake") batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}, {envs = {LD_LIBRARY_PATH="/xxx"}}) batchcmds:mkdir("/xxx") -- and cp, mv, rm, ln .. batchcmds:compile(sourcefile_cx, objectfile, {configs = {includedirs = sourcefile_dir, languages = (sourcekind == "cxx" and "c++11")}}) batchcmds:link(objectfiles, targetfile, {configs = {linkdirs = ""}}) ``` At the same time, we also simplify the configuration of dependency execution in it. The following is a complete example: ```lua rule("lex") set_extensions(".l", ".ll") on_buildcmd_file(function (target, batchcmds, sourcefile_lex, opt) -- imports import("lib.detect.find_tool") -- get lex local lex = assert(find_tool("flex") or find_tool("lex"), "lex not found!") -- get c/c++ source file for lex local extension = path.extension(sourcefile_lex) local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c")) -- add objectfile local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) -- add commands batchcmds:show_progress(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex) batchcmds:mkdir(path.directory(sourcefile_cx)) batchcmds:vrunv(lex.program, {"-o", sourcefile_cx, sourcefile_lex}) batchcmds:compile(sourcefile_cx, objectfile) -- add deps batchcmds:add_depfiles(sourcefile_lex) batchcmds:set_depmtime(os.mtime(objectfile)) batchcmds:set_depcache(target:dependfile(objectfile)) end) ``` As we can see from the above configuration, the overall execution command list is very clear, and if we use `on_build_file` to implement it, we can compare the configuration of the previous rule, and we can intuitively feel that the configuration of the new interface is indeed simplified a lot. ```lua rule("lex") -- set extension set_extensions(".l", ".ll") -- load lex/flex before_load(function (target) import("core.project.config") import("lib.detect.find_tool") local lex = config.get("__lex") if not lex then lex = find_tool("flex") or find_tool("lex") if lex and lex.program then config.set("__lex", lex.program) cprint("checking for Lex ... ${color.success}%s", lex.program) else cprint("checking for Lex ... ${color.nothing}${text.nothing}") raise("lex/flex not found!") end end end) -- build lex file on_build_file(function (target, sourcefile_lex, opt) -- imports import("core.base.option") import("core.theme.theme") import("core.project.config") import("core.project.depend") import("core.tool.compiler") import("private.utils.progress") -- get lex local lex = assert(config.get("__lex"), "lex not found!") -- get extension: .l/.ll local extension = path.extension(sourcefile_lex) -- get c/c++ source file for lex local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c")) local sourcefile_dir = path.directory(sourcefile_cx) -- get object file local objectfile = target:objectfile(sourcefile_cx) -- load compiler local compinst = compiler.load((extension == ".ll" and "cxx" or "cc"), {target = target}) -- get compile flags local compflags = compinst:compflags({target = target, sourcefile = sourcefile_cx}) -- add objectfile table.insert(target:objectfiles(), objectfile) -- load dependent info local dependfile = target:dependfile(objectfile) local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {}) -- need build this object? local depvalues = {compinst:program(), compflags} if not depend.is_changed(dependinfo, {lastmtime = os.mtime(objectfile), values = depvalues}) then return end -- trace progress info progress.show(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex) -- ensure the source file directory if not os.isdir(sourcefile_dir) then os.mkdir(sourcefile_dir) end -- compile lex os.vrunv(lex, {"-o", sourcefile_cx, sourcefile_lex}) -- trace if option.get("verbose") then print(compinst:compcmd(sourcefile_cx, objectfile, {compflags = compflags})) end -- compile c/c++ source file for lex dependinfo.files = {} assert(compinst:compile(sourcefile_cx, objectfile, {dependinfo = dependinfo, compflags = compflags})) -- update files and values to the dependent file dependinfo.values = depvalues table.insert(dependinfo.files, sourcefile_lex) depend.save(dependinfo, dependfile) end) ``` For a detailed description and background of this, see: [issue 1246](https://github.com/xmake-io/xmake/issues/1246) ### Dependent package configuration improvements #### Use add\_extsources to improve package name lookup Regarding the definition of remote dependency packages, we have also added two configuration interfaces `add_extsources` and `on_fetch`, which can better configure xmake to search for system libraries during the process of installing C/C++ packages. As for the specific background, we can give an example. For example, we added a package of `package("libusb")` to the [xmake-repo](https://github.com/xmake-io/xmake-repo) repository . Then users can directly integrate and use it in the following ways: ```lua add_requires("libusb") target("test") set_kind("binary") add_files("src/*.c") add_packages("libusb") ``` If libusb is not installed on the user's system, xmake will automatically download the libusb library source code, automatically compile, install and integrate, and there is no problem. But if the user installs the libusb library to the system through `apt install libusb-1.0`, then xmake should automatically search for the libusb package installed by the user in the system environment first, and use it directly, avoiding additional download, compilation and installation. But here comes the problem, xmake internally passes `find_package("libusb")` and fails to find it. Why is that? Because the package name of libusb installed via apt is `libusb-1.0`, not libusb. We can only find it through `pkg-config --cflags libusb-1.0`, but the default find\_package logic inside xmake doesn't know the existence of `libusb-1.0`, so it can't be found. Therefore, in order to better adapt to the search of system libraries in different system environments, we can use `add_extsources("pkgconfig::libusb-1.0")` to let xmake improve the search logic, for example: ```lua package("libusb") add_extsources("pkgconfig::libusb-1.0") on_install(function (package) -- ... end) ``` In addition, we can also use this method to improve the search for packages installed by other package managers such as homebrew/pacman, for example: `add_extsources("pacman::libusb-1.0")`. #### Use on\_fetch to improve find system libraries If the system libraries installed under different systems only have different package names, it is enough to use `add_extsources` to improve the system library search, which is simple and convenient. However, if some packages are installed in the system, the location is more complicated. To find them, some additional scripts may be needed. For example: access to the registry under windows to find packages, etc. At this time, we can use `on_fetch `Fully customized search system library logic. Let's take libusb as an example. Instead of `add_extsources`, we can use the following method to achieve the same effect. Of course, we can do more things in it. ```lua package("libusb") on_fetch("linux", function(package, opt) if opt.system then return find_package("pkgconfig::libusb-1.0") end end) ``` ### Support manifest file on windows In the new version, we also added support for windows `.manifest` files, just add it through `add_files`. ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") add_files("src/*.manifest") ``` ### Improve xrepo command Regarding the xrepo command, we have also improved it slightly. Now you can batch uninstall and delete the installed packages through the following command, and support pattern matching: ```bash $ xrepo remove --all $ xrepo remove --all zlib pcr* ``` ### Support to export package configuration We have also improved `add_packages` so that it also supports `{public = true}` to export package configuration to the parent target. ```lua add_rules("mode.debug", "mode.release") add_requires("pcre2") target("test") set_kind("shared") add_packages("pcre2", {public = true}) add_files("src/test.cpp") target("demo") add_deps("test") set_kind("binary") add_files("src/main.cpp") -- here we can use the pcre2 library exported by the test target ``` it's private by default, but links/linkdirs will still be exported automatically ```lua add_packages("pcre2") ``` it will export all, contains includedirs, defines ```lua add_packages("pcre2", {public = true}) ``` ## Changelog ### New features * [#955](https://github.com/xmake-io/xmake/issues/955#issuecomment-766481512): Support `zig cc` and `zig c++` as c/c++ compiler * [#955](https://github.com/xmake-io/xmake/issues/955#issuecomment-768193083): Support zig cross-compilation * [#1177](https://github.com/xmake-io/xmake/issues/1177): Improve to detect terminal and color codes * [#1216](https://github.com/xmake-io/xmake/issues/1216): Pass custom configuration scripts to xrepo * Add linuxos builtin module to get linux system information * [#1217](https://github.com/xmake-io/xmake/issues/1217): Support to fetch remote toolchain package when building project * [#1123](https://github.com/xmake-io/xmake/issues/1123): Add `rule("utils.symbols.export_all")` to export all symbols for windows/dll * [#1181](https://github.com/xmake-io/xmake/issues/1181): Add `utils.platform.gnu2mslib(mslib, gnulib)` module api to convert mingw/xxx.dll.a to msvc xxx.lib * [#1246](https://github.com/xmake-io/xmake/issues/1246): Improve rules and generators to support commands list * [#1239](https://github.com/xmake-io/xmake/issues/1239): Add `add_extsources` to improve find external packages * [#1241](https://github.com/xmake-io/xmake/issues/1241): Support add .manifest files for windows program * Support to use `xrepo remove --all` to remove all packages * [#1254](https://github.com/xmake-io/xmake/issues/1254): Support to export packages to parent target ### Change * [#1226](https://github.com/xmake-io/xmake/issues/1226): Add missing qt include directories * [#1183](https://github.com/xmake-io/xmake/issues/1183): Improve c++ lanuages to support Qt6 * [#1237](https://github.com/xmake-io/xmake/issues/1237): Add qt.ui files for vsxmake plugin * Improve vs/vsxmake plugins to support precompiled header and intellisense * [#1090](https://github.com/xmake-io/xmake/issues/1090): Simplify integration of custom code generators * [#1065](https://github.com/xmake-io/xmake/issues/1065): Improve protobuf rule to support compile\_commands generators * [#1249](https://github.com/xmake-io/xmake/issues/1249): Improve vs/vsxmake generator to support startproject * [#605](https://github.com/xmake-io/xmake/issues/605): Improve to link orders for add\_deps/add\_packages * Remove deprecated `add_defines_h_if_ok` and `add_defines_h` apis for option ### Bugs fixed * [#1219](https://github.com/xmake-io/xmake/issues/1219): Fix version check and update * [#1235](https://github.com/xmake-io/xmake/issues/1235): Fix include directories with spaces --- --- url: /zh/posts/xmake-update-v2.5.2.md --- 在 2.5.2 版本中,我们增加了一个重量级的新特性:`自动拉取远程交叉编译工具链`。 这是用来干什么的呢,做过交叉编译以及有 C/C++ 项目移植经验的同学应该知道,折腾各种交叉编译工具链,移植编译项目是非常麻烦的一件事,需要自己下载对应工具链,并且配置工具链和编译环境很容易出错导致编译失败。 现在,xmake 已经可以支持自动下载项目所需的工具链,然后使用对应工具链直接编译项目,用户不需要关心如何配置工具链,任何情况下只需要执行 `xmake` 命令即可完成编译。 ![](/assets/img/posts/xmake/muslcc.gif) 甚至对于 C/C++ 依赖包的集成,也可以自动切换到对应工具链编译安装集成,一切完全自动化,完全不需要用户操心。 除了交叉编译工具链,我们也可以自动拉取工具链,比如特定版本的 llvm,llvm-mingw, zig 等各种工具链来参与编译 C/C++/Zig 项目的编译。 即使是 cmake 也还不支持工具链的自动拉取,顶多只能配合 vcpkg/conan 等第三方包管理对 C/C++ 依赖包进行集成,另外,即使对于 C/C++依赖包,xmake 也有自己原生内置的包管理工具,完全无任何依赖。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) * [入门课程](https://xmake.io/zh/) ## 新特性介绍 ### 自动拉取远程交叉编译工具链 从 2.5.2 版本开始,我们可以拉取指定的工具链来集成编译项目,我们也支持将依赖包切换到对应的远程工具链参与编译后集成进来。 相关例子代码见:[Toolchain/Packages Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/package) 相关的 issue [#1217](https://github.com/xmake-io/xmake/issues/1217) 当前,我们已经在 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库收录了以下工具链包,可以让 xmake 远程拉取集成: * llvm * llvm-mingw * gnu-rm * muslcc * zig 虽然现在支持的工具链包不多,当但是整体架构已经打通,后期我们只需要收录更多的工具链进来就行,比如:gcc, tinyc, vs-buildtools 等工具链。 由于 xmake 的包支持语义版本,因此如果项目依赖特定版本的 gcc/clang 编译器,就不要用户去折腾安装了,xmake 会自动检测当前系统的 gcc/clang 版本是否满足需求。 如果版本不满足,那么 xmake 就会走远程拉取,自动帮用户安装集成特定版本的 gcc/clang,然后再去编译项目。 #### 拉取指定版本的 llvm 工具链 我们使用 llvm-10 中的 clang 来编译项目。 ```lua add_requires("llvm 10.x", {alias = "llvm-10"}) target("test") set_kind("binary") add_files("src/*.c) set_toolchains("llvm@llvm-10") ``` 其中,`llvm@llvm-10` 的前半部分为工具链名,也就是 `toolchain("llvm")`,后面的名字是需要被关联工具链包名,也就是 `package("llvm")`,不过如果设置了别名,那么会优先使用别名:`llvm-10` 另外,我们后续还会增加 gcc 工具链包到 xmake-repo,使得用户可以自由切换 gcc-10, gcc-11 等特定版本的 gcc 编译器,而无需用户去手动安装。 ### 拉取交叉编译工具链 我们也可以拉取指定的交叉编译工具链来编译项目。 ```lua add_requires("muslcc") target("test") set_kind("binary") add_files("src/*.c) set_toolchains("@muslcc") ``` muslcc 是 https://musl.cc 提供的一款交叉编译工具链,默认 xmake 会自动集成编译 `x86_64-linux-musl-` 目标平台。 当然,我们也可以通过 `xmake f -a arm64` 切换到 `aarch64-linux-musl-` 目标平台来进行交叉编译。 #### 拉取工具链并且集成对应工具链编译的依赖包 我们也可以使用指定的muslcc交叉编译工具链去编译和集成所有的依赖包。 ```lua add_requires("muslcc") add_requires("zlib", "libogg", {system = false}) set_toolchains("@muslcc") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libogg") ``` 这个时候,工程里面配置的 zlib, libogg 等依赖包,也会切换使用 muslcc 工具链,自动下载编译然后集成进来。 我们也可以通过 `set_plat/set_arch` 固定平台,这样只需要一个 xmake 命令,就可以完成整个交叉编译环境的集成以及架构切换。 ```lua add_requires("muslcc") add_requires("zlib", "libogg", {system = false}) set_plat("cross") set_arch("arm64") set_toolchains("@muslcc") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libogg") ``` 完整例子见:[Examples (muslcc)](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/toolchain_muslcc/xmake.lua) #### 拉取集成 Zig 工具链 xmake 会先下载特定版本的 zig 工具链,然后使用此工具链编译 zig 项目,当然用户如果已经自行安装了 zig 工具链,xmake 也会自动检测对应版本是否满足,如果满足需求,那么会直接使用它,无需重复下载安装。 ```lua add_rules("mode.debug", "mode.release") add_requires("zig 0.7.x") target("test") set_kind("binary") add_files("src/*.zig") set_toolchains("@zig") ``` ### 增加对 zig cc 编译器支持 `zig cc` 是 zig 内置的 c/c++ 编译器,可以完全独立进行 c/c++ 代码的编译和链接,完全不依赖 gcc/clang/msvc,非常给力。 因此,我们完全可以使用它来编译 c/c++ 项目,关键是 zig 的工具链还非常轻量,仅仅几十M 。 我们只需要切换到 zig 工具链即可完成编译: ```bash $ xmake f --toolchain=zig $ xmake [ 25%]: compiling.release src/main.c "zig cc" -c -arch x86_64 -fvisibility=hidden -O3 -DNDEBUG -o build/.objs/xmake_test/macosx/x86_64/release/src/main.c.o src/main.c [ 50%]: linking.release test "zig c++" -o build/macosx/x86_64/release/test build/.objs/xmake_test/macosx/x86_64/release/src/main.c.o -arch x86_64 -stdlib=libc++ -Wl,-x -lz [100%]: build ok! ``` 另外,`zig cc` 的另外一个强大之处在于,它还支持不同架构的交叉编译,太 happy 了。 通过 xmake,我们也只需再额外切换下架构到 arm64,即可实现对 arm64 的交叉编译,例如: ```bash $ xmake f -a arm64 --toolchain=zig $ xmake [ 25%]: compiling.release src/main.c "zig cc" -c -target aarch64-macos-gnu -arch arm64 -fvisibility=hidden -O3 -DNDEBUG -o build/.objs/xmake_test/macosx/arm64/release/src/main.c.o src/main.c checking for flags (-MMD -MF) ... ok checking for flags (-fdiagnostics-color=always) ... ok [ 50%]: linking.release xmake_test "zig c++" -o build/macosx/arm64/release/xmake_test build/.objs/xmake_test/macosx/arm64/release/src/main.c.o -target aarch64-macos-gnu -arch arm64 -stdlib=libc++ -Wl,-x -lz [100%]: build ok! ``` 即使你是在在 macOS,也可以用 `zig cc` 去交叉编译 windows/x64 目标程序,相当于替代了 mingw 干的事情。 ```bash $ xmake f -p windows -a x64 --toolchain=zig $ xmake ``` ### 自动导出所有 windows/dll 中的符号 cmake 中有这样一个功能:`WINDOWS_EXPORT_ALL_SYMBOLS`,安装 cmake 文档中的说法: > Enable this boolean property to automatically create a module definition (.def) file with all global symbols found > in the input .obj files for a SHARED library (or executable with ENABLE\_EXPORTS) on Windows. > The module definition file will be passed to the linker causing all symbols to be exported from the .dll. For global data symbols, > \_\_declspec(dllimport) must still be used when compiling against the code in the .dll. All other function symbols will be automatically exported and imported by callers. > This simplifies porting projects to Windows by reducing the need for explicit dllexport markup, even in C++ classes. 大体意思就是: > 启用此布尔属性,可以自动创建一个模块定义(.def)文件,其中包含在Windows上的共享库(或使用ENABLE\_EXPORTS的可执行文件)的输入.obj文件中找到的所有全局符号。 > 模块定义文件将被传递给链接器,使所有符号从.dll中导出。对于全局数据符号,当对.dll中的代码进行编译时,仍然必须使用\_\_declspec(dllimport)。 > 所有其它的函数符号将被调用者自动导出和导入。这就简化了将项目移植到 Windows 的过程,减少了对显式 dllexport 标记的需求,甚至在 C++ 类中也是如此。 现在,xmake 中也提供了类似的特性,可以快速全量导出 windows/dll 中的符号,来简化对第三方项目移植过程中,对符号导出的处理。另外,如果项目中的符号太多,也可以用此来简化代码中的显式导出需求。 我们只需在对应生成的 dll 目标上,配置 `utils.symbols.export_all` 规则即可。 ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_all") target("test") set_kind("binary") add_deps("foo") add_files("src/main.c") ``` xmake 会自动扫描所有的 obj 对象文件,然后生成 def 符号导出文件,传入 link.exe 实现快速全量导出。 ### 转换 mingw/.dll.a 到 msvc/.lib 这个特性也是参考自 CMAKE\_GNUtoMS 功能,可以把MinGW生成的动态库(xxx.dll & xxx.dll.a)转换成Visual Studio可以识别的格式(xxx.dll & xxx.lib),从而实现混合编译。 这个功能对Fortran & C++混合项目特别有帮助,因为VS不提供fortran编译器,只能用MinGW的gfortran来编译fortran部分,然后和VS的项目链接。 往往这样的项目同时有一些其他的库以vs格式提供,因此纯用MinGW编译也不行,只能使用cmake的这个功能来混合编译。 因此,xmake 也提供了一个辅助模块接口去支持它,使用方式如下: ```lua import("utils.platform.gnu2mslib") gnu2mslib("xxx.lib", "xxx.dll.a") gnu2mslib("xxx.lib", "xxx.def") gnu2mslib("xxx.lib", "xxx.dll.a", {dllname = "xxx.dll", arch = "x64"}) ``` 支持从 def 生成 xxx.lib ,也支持从 xxx.dll.a 自动导出 .def ,然后再生成 xxx.lib 具体细节见:[issue #1181](https://github.com/xmake-io/xmake/issues/1181) ### 实现批处理命令来简化自定义规则 为了简化用户自定义 rule 的配置,xmake 新提供了 `on_buildcmd_file`, `on_buildcmd_files` 等自定义脚本入口, 我们可以通过 batchcmds 对象,构造一个批处理命令行任务,xmake 在实际执行构建的时候,一次性执行这些命令。 这对于 `xmake project` 此类工程生成器插件非常有用,因为生成器生成的第三方工程文件并不支持 `on_build_files` 此类内置脚本的执行支持。 但是 `on_buildcmd_file` 构造的最终结果,就是一批原始的 cmd 命令行,可以直接给其他工程文件作为 custom commands 来执行。 另外,相比 `on_build_file`,它也简化对扩展文件的编译实现,更加的可读易配置,对用户也更加友好。 ```lua rule("foo") set_extensions(".xxx") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}) end) ``` 除了 `batchcmds:vrunv`,我们还支持一些其他的批处理命令,例如: ```lua batchcmds:show("hello %s", "xmake") batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}, {envs = {LD_LIBRARY_PATH="/xxx"}}) batchcmds:mkdir("/xxx") -- and cp, mv, rm, ln .. batchcmds:compile(sourcefile_cx, objectfile, {configs = {includedirs = sourcefile_dir, languages = (sourcekind == "cxx" and "c++11")}}) batchcmds:link(objectfiles, targetfile, {configs = {linkdirs = ""}}) ``` 同时,我们在里面也简化对依赖执行的配置,下面是一个完整例子: ```lua rule("lex") set_extensions(".l", ".ll") on_buildcmd_file(function (target, batchcmds, sourcefile_lex, opt) -- imports import("lib.detect.find_tool") -- get lex local lex = assert(find_tool("flex") or find_tool("lex"), "lex not found!") -- get c/c++ source file for lex local extension = path.extension(sourcefile_lex) local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c")) -- add objectfile local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) -- add commands batchcmds:show_progress(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex) batchcmds:mkdir(path.directory(sourcefile_cx)) batchcmds:vrunv(lex.program, {"-o", sourcefile_cx, sourcefile_lex}) batchcmds:compile(sourcefile_cx, objectfile) -- add deps batchcmds:add_depfiles(sourcefile_lex) batchcmds:set_depmtime(os.mtime(objectfile)) batchcmds:set_depcache(target:dependfile(objectfile)) end) ``` 我们从上面的配置可以看到,整体执行命令列表非常清晰,而如果我们用 `on_build_file` 来实现,可以对比下之前这个规则的配置,就能直观感受到新接口的配置方式确实简化了不少: ```lua rule("lex") -- set extension set_extensions(".l", ".ll") -- load lex/flex before_load(function (target) import("core.project.config") import("lib.detect.find_tool") local lex = config.get("__lex") if not lex then lex = find_tool("flex") or find_tool("lex") if lex and lex.program then config.set("__lex", lex.program) cprint("checking for Lex ... ${color.success}%s", lex.program) else cprint("checking for Lex ... ${color.nothing}${text.nothing}") raise("lex/flex not found!") end end end) -- build lex file on_build_file(function (target, sourcefile_lex, opt) -- imports import("core.base.option") import("core.theme.theme") import("core.project.config") import("core.project.depend") import("core.tool.compiler") import("private.utils.progress") -- get lex local lex = assert(config.get("__lex"), "lex not found!") -- get extension: .l/.ll local extension = path.extension(sourcefile_lex) -- get c/c++ source file for lex local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c")) local sourcefile_dir = path.directory(sourcefile_cx) -- get object file local objectfile = target:objectfile(sourcefile_cx) -- load compiler local compinst = compiler.load((extension == ".ll" and "cxx" or "cc"), {target = target}) -- get compile flags local compflags = compinst:compflags({target = target, sourcefile = sourcefile_cx}) -- add objectfile table.insert(target:objectfiles(), objectfile) -- load dependent info local dependfile = target:dependfile(objectfile) local dependinfo = option.get("rebuild") and {} or (depend.load(dependfile) or {}) -- need build this object? local depvalues = {compinst:program(), compflags} if not depend.is_changed(dependinfo, {lastmtime = os.mtime(objectfile), values = depvalues}) then return end -- trace progress info progress.show(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex) -- ensure the source file directory if not os.isdir(sourcefile_dir) then os.mkdir(sourcefile_dir) end -- compile lex os.vrunv(lex, {"-o", sourcefile_cx, sourcefile_lex}) -- trace if option.get("verbose") then print(compinst:compcmd(sourcefile_cx, objectfile, {compflags = compflags})) end -- compile c/c++ source file for lex dependinfo.files = {} assert(compinst:compile(sourcefile_cx, objectfile, {dependinfo = dependinfo, compflags = compflags})) -- update files and values to the dependent file dependinfo.values = depvalues table.insert(dependinfo.files, sourcefile_lex) depend.save(dependinfo, dependfile) end) ``` 关于这个的详细说明和背景,见:[issue 1246](https://github.com/xmake-io/xmake/issues/1246) ### 依赖包配置改进 #### 使用 add\_extsources 改进包名查找 关于远程依赖包定义这块,我们也新增了 `add_extsources` 和 `on_fetch` 两个配置接口,可以更好的配置 xmake 在安装 C/C++ 包的过程中,对系统库的查找过程。 至于具体背景,我们可以举个例子,比如我们在 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库新增了一个 `package("libusb")` 的包。 那么用户就可以通过下面的方式,直接集成使用它: ```lua add_requires("libusb") target("test") set_kind("binary") add_files("src/*.c") add_packages("libusb") ``` 如果用户系统上确实没有安装 libusb,那么 xmake 会自动下载 libusb 库源码,自动编译安装集成,没啥问题。 但如果用户通过 `apt install libusb-1.0` 安装了 libusb 库到系统,那么按理 xmake 应该会自动优先查找用户安装到系统环境的 libusb 包,直接使用,避免额外的下载编译安装。 但是问题来了,xmake 内部通过 `find_package("libusb")` 并没有找打它,这是为什么呢?因为通过 apt 安装的 libusb 包名是 `libusb-1.0`, 而不是 libusb。 我们只能通过 `pkg-config --cflags libusb-1.0` 才能找到它,但是 xmake 内部的默认 find\_package 逻辑并不知道 `libusb-1.0` 的存在,所以找不到。 因此为了更好地适配不同系统环境下,系统库的查找,我们可以通过 `add_extsources("pkgconfig::libusb-1.0")` 去让 xmake 改进查找逻辑,例如: ```lua package("libusb") add_extsources("pkgconfig::libusb-1.0") on_install(function (package) -- ... end) ``` 另外,我们也可以通过这个方式,改进查找 homebrew/pacman 等其他包管理器安装的包,例如:`add_extsources("pacman::libusb-1.0")`。 #### 使用 on\_fetch 完全定制系统库查找 如果不同系统下安装的系统库,仅仅只是包名不同,那么使用 `add_extsources` 改进系统库查找已经足够,简单方便。 但是如果有些安装到系统的包,位置更加复杂,想要找到它们,也许需要一些额外的脚本才能实现,例如:windows 下注册表的访问去查找包等等,这个时候,我们就可以通过 `on_fetch` 完全定制化查找系统库逻辑。 还是以 libusb 为例,我们不用 `add_extsources`,可以使用下面的方式,实现相同的效果,当然,我们可以在里面做更多的事情。 ``` package("libusb") on_fetch("linux", function(package, opt) if opt.system then return find_package("pkgconfig::libusb-1.0") end end) ``` ### manifest 文件支持 在新版本中,我们还新增了对 windows `.manifest` 文件的支持,只需要通过 `add_files` 添加进来即可。 ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") add_files("src/*.manifest") ``` ### xrepo 命令改进 关于 xrepo 命令,我们也稍微改进了下,现在可以通过下面的命令,批量卸载删除已经安装的包,支持模式匹配: ```bash $ xrepo remove --all $ xrepo remove --all zlib pcr* ``` ### 包的依赖导出支持 我们也改进了 `add_packages`,使其也支持 `{public = true}` 来导出包配置给父 target。 ```lua add_rules("mode.debug", "mode.release") add_requires("pcre2") target("test") set_kind("shared") add_packages("pcre2", {public = true}) add_files("src/test.cpp") target("demo") add_deps("test") set_kind("binary") add_files("src/main.cpp") -- 我们可以在这里使用被 test 目标导出 pcre2 库 ``` 至于具体导出哪些配置呢? ```lua -- 默认私有,但是 links/linkdirs 还是会自动导出 add_packages("pcre2") -- 全部导出。包括 includedirs, defines add_packages("pcre2", {public = true}) ``` ## 更新内容 ### 新特性 * [#955](https://github.com/xmake-io/xmake/issues/955#issuecomment-766481512): 支持 `zig cc` 和 `zig c++` 作为 c/c++ 编译器 * [#955](https://github.com/xmake-io/xmake/issues/955#issuecomment-768193083): 支持使用 zig 进行交叉编译 * [#1177](https://github.com/xmake-io/xmake/issues/1177): 改进终端和 color codes 探测 * [#1216](https://github.com/xmake-io/xmake/issues/1216): 传递自定义 includes 脚本给 xrepo * 添加 linuxos 内置模块获取 linux 系统信息 * [#1217](https://github.com/xmake-io/xmake/issues/1217): 支持当编译项目时自动拉取工具链 * [#1123](https://github.com/xmake-io/xmake/issues/1123): 添加 `rule("utils.symbols.export_all")` 自动导出所有 windows/dll 中的符号 * [#1181](https://github.com/xmake-io/xmake/issues/1181): 添加 `utils.platform.gnu2mslib(mslib, gnulib)` 模块接口去转换 mingw/xxx.dll.a 到 msvc xxx.lib * [#1246](https://github.com/xmake-io/xmake/issues/1246): 改进规则支持新的批处理命令去简化自定义规则实现 * [#1239](https://github.com/xmake-io/xmake/issues/1239): 添加 `add_extsources` 去改进外部包的查找 * [#1241](https://github.com/xmake-io/xmake/issues/1241): 支持为 windows 程序添加 .manifest 文件参与链接 * 支持使用 `xrepo remove --all` 命令去移除所有的包,并且支持模式匹配 * [#1254](https://github.com/xmake-io/xmake/issues/1254): 支持导出包配置给父 target,实现包配置的依赖继承 ### 改进 * [#1226](https://github.com/xmake-io/xmake/issues/1226): 添加缺失的 Qt 头文件搜索路径 * [#1183](https://github.com/xmake-io/xmake/issues/1183): 改进 C++ 语言标准,以便支持 Qt6 * [#1237](https://github.com/xmake-io/xmake/issues/1237): 为 vsxmake 插件添加 qt.ui 文件 * 改进 vs/vsxmake 插件去支持预编译头文件和智能提示 * [#1090](https://github.com/xmake-io/xmake/issues/1090): 简化自定义规则 * [#1065](https://github.com/xmake-io/xmake/issues/1065): 改进 protobuf 规则,支持 compile\_commands 生成器 * [#1249](https://github.com/xmake-io/xmake/issues/1249): 改进 vs/vsxmake 生成器去支持启动工程设置 * [#605](https://github.com/xmake-io/xmake/issues/605): 改进 add\_deps 和 add\_packages 直接的导出 links 顺序 * 移除废弃的 `add_defines_h_if_ok` and `add_defines_h` 接口 ### Bugs 修复 * [#1219](https://github.com/xmake-io/xmake/issues/1219): 修复版本检测和更新 * [#1235](https://github.com/xmake-io/xmake/issues/1235): 修复 includes 搜索路径中带有空格编译不过问题 --- --- url: /posts/xmake-update-v2.5.3.md --- ### Build a Linux Bpf program In the new version, we started to support the compilation of bpf programs, as well as linux and android platforms, and can automatically pull the llvm and android ndk toolchains. For more details, please see: [#1274](https://github.com/xmake-io/xmake/issues/1274) The build configuration is as follows, it's very simple. If we do not need to build the android version, we can remove some configuration about android. ```lua add_rules("mode.release", "mode.debug") add_rules("platform.linux.bpf") add_requires("linux-tools", {configs = {bpftool = true}}) add_requires("libbpf") if is_plat("android") then add_requires("ndk >=22.x") set_toolchains("@ndk", {sdkver = "23"}) else add_requires("llvm >=10.x") set_toolchains("@llvm") add_requires("linux-headers") end target("minimal") set_kind("binary") add_files("src/*.c") add_packages("linux-tools", "linux-headers", "libbpf") set_license("GPL-2.0") ``` Through the above configuration, we can probably see that we have integrated and configured specific versions of llvm and NDK toolchains, as well as packages such as libbpf, linux-headers, linux-tools, etc.. xmake will automatically pull them, and then use the corresponding toolchain to integrate and compile these dependent packages, and finally generate the bpf program. The linux-tools package mainly uses the libtool program inside to generate the bpf skeleton header file, and xmake will automatically run this tool to generate it. #### Compile linux bpf program We only need to execute the xmake command to complete the compilation, even if you have not installed llvm/clang. Of course, if you have already installed them, if it's version is matched, xmake will also be used first. ```bash $ xmake ``` We can also use `xmake -v` to compile and view the complete and detailed compilation commands: ```bash $ xmake -v [20%]: compiling.bpf src/minimal.bpf.c /usr/bin/ccache /usr/bin/clang -c -Qunused-arguments -m64 -fvisibility=hidden -O3 -Ibuild/.gens/minimal/linux/x86_64/release/rules/bpf -isystem /home/ruki/ .xmake/packages/l/linux-tools/5.9.16/0c52e491268946fe9a4bc91d4906d66b/include -isystem /home/ruki/.xmake/packages/z/zlib/1.2.11/3a7e4427eda94fc69fad0009a1629fd8/include -isystem /home/ruki/.xmake /packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include -isystem /home/ruki/.xmake/packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include/libelf -isystem /home/ruki/.xmake /l/libcap/2.27/c55b28aa3b3745489b93895d0d606ed1/include -isystem /home/ruki/.xmake/packages/l/linux-headers/5.9.16/8e3a440cbe1f42249aef3d89f1528ecb/include -DNDEBUG -target bpf -g -o build/.gens/.gens /linux/x86_64/release/rules/bpf/minimal.bpf.o src/minimal.bpf.c llvm-strip -g build/.gens/minimal/linux/x86_64/release/rules/bpf/minimal.bpf.o bpftool gen skeleton build/.gens/minimal/linux/x86_64/release/rules/bpf/minimal.bpf.o [40%]: ccache compiling.release src/minimal.c /usr/bin/ccache /usr/bin/clang -c -Qunused-arguments -m64 -fvisibility=hidden -O3 -Ibuild/.gens/minimal/linux/x86_64/release/rules/bpf -isystem /home/ruki/ .xmake/packages/l/linux-tools/5.9.16/0c52e491268946fe9a4bc91d4906d66b/include -isystem /home/ruki/.xmake/packages/z/zlib/1.2.11/3a7e4427eda94fc69fad0009a1629fd8/include -isystem /home/ruki/.xmake /packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include -isystem /home/ruki/.xmake/packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include/libelf -isystem /home/ruki/.xmake /l/libcap/2.27/c55b28aa3b3745489b93895d0d606ed1/include -isystem /home/ruki/.xmake/packages/l/linux-headers/5.9.16/8e3a440cbe1f42249aef3d89f1528ecb/include -DNDEBUG -o build/.objs/minimal/linux/x86_64/ release/src/minimal.co src/minimal.c [60%]: linking.release minimal /usr/bin/clang++ -o build/linux/x86_64/release/minimal build/.objs/minimal/linux/x86_64/release/src/minimal.co -m64 -L/home/ruki/.xmake/packages/l /linux-tools/5.9.16/0c52e491268946fe9a4bc91d4906d66b/lib64 -L/home/ruki/.xmake/packages/z/zlib/1.2.11/3a7e4427eda94fc69fad0009a1629fd8/lib -L/home/ruki/.xmake/packages/l/libelf /0.8.13/ced4fdd8151a475dafc5f51e2a031997/lib -L/home/ruki/.xmake/packages/l/libcap/2.27/c55b28aa3b3745489b93895d0d606ed1/lib -s -lbpf -lz -lelf -lcap [100%]: build ok! ``` #### Compile Android bpf program If we compile the Android version, we only need to switch to the android platform, which is also very convenient ```bash $ xmake f -p android $ xmake ``` xmake will automatically download the ndk tool chain and the corresponding android version libbpf and other libraries to use. ```bash $ xmake f -p android -c checking for architecture ... armeabi-v7a checking for Android SDK directory ... no checking for NDK directory ... no note: try installing these packages (pass -y to skip confirm)? in local-repo: -> libcap 2.27 [linux, x86_64, from:linux-tools] -> libelf 0.8.13 [linux, x86_64, from:linux-tools] -> zlib 1.2.11 [linux, x86_64, from:linux-tools] -> linux-tools 5.9.16 [bpftool:y] -> ndk 22.0 -> libelf#1 0.8.13 [toolchains:@ndk, from:libbpf] -> zlib#1 1.2.11 [toolchains:@ndk, from:libbpf] -> libbpf v0.3 [toolchains:@ndk] please input: y (y/n) => install libcap 2.27 .. ok => install zlib 1.2.11 .. ok => install libelf 0.8.13 .. ok => install ndk 22.0 .. ok => install linux-tools 5.9.16 .. ok => install libelf#1 0.8.13 .. ok => install zlib#1 1.2.11 .. ok => install libbpf v0.3 .. ok ruki@010689392c4d:/mnt/bpf_minimal$ xmake [20%]: compiling.bpf src/minimal.bpf.c [40%]: ccache compiling.release src/minimal.c [60%]: linking.release minimal [100%]: build ok! ``` Of course, if you have manually downloaded the corresponding version of the ndk toolchain, we can also specify it to use it instead of automatically pulling it. ```bash $ xmake f -p android --ndk=/xxx/android-ndk-r22 $ xmake ``` However, if you download it yourself, remember to download at least the version above ndk r22, because clang in the lower version of ndk does not support the compilation and generation of bpf programs. Finally, here is a complete bpf scaffolding project based on xmake, you can refer to: In addition, there is also a minimal bpf example program: https://github.com/xmake-io/xmake/tree/master/tests/projects/bpf/minimal ### Integrate and use Conda package Conda is a very powerful third-party package manager that supports binary package pull in various languages. Here we only use the C/C++ package inside. Its integrated usage is similar to conan/vcpkg, except that the package namespace is changed to `conda::` ```lua add_requires("conda::libpng 1.6.37", {alias = "libpng"}) add_requires("conda::openssl") target("testco") set_kind("binary") add_files("src/*.cpp") add_packages("libpng", "conda::openssl") ``` Note: Although we support many third-party package managers, such as conan/conda/vcpkg/brew, etc., xmake also has its own package repository management. There are currently nearly 300 commonly used packages that support different platforms, some of which are The package also supports android/ios/mingw and even cross-compilation environment. Therefore, if the official [xmake-repo](https://github.com/xmake-io/xmake-repo) repository already provides the required package, you can use it directly without specifying the package namespace. ### Get host cpu information In the current version, we have added a new `core.base.cpu` module and `os.cpuinfo` interface to obtain various information about the cpu, such as: cpu family/model, microarchitecture, core number, features and other information. This is usually very useful in projects that pursue performance. These projects usually need to be optimized according to the CPU's memory model and extended instruction set. At the same time, if you want to cross-platform, you need to specify the corresponding code according to the current cpu information, (for example, after intel haswell) One set, one set after amd zen, the older ones default to no optimization). This information is also used in many high-performance computing libraries. Therefore, through this module interface, you can obtain the current host cpu information and feature support when compiling and configuring, so as to enable relevant optimized compilation. We can quickly obtain all information through `os.cpuinfo()`, or specify `os.cpuinfo("march")` to obtain specific information, such as march, which is microarchitecture We can also use the `xmake l` command to quickly view the obtained results. ```bash $ xmake l os.cpuinfo { features = "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clfsh ds acpi mmx fxsr sse sse2 ss htt tm pbe sse3 pclmulqdq dtes64 mon dscpl vmx tse64 mon dscpl vmx tse tm2 s sse4_1 sse4_2 x2apic movbe popcnt aes pcid xsave osxsave seglim64 tsctmr avx1_0 rdrand f16c", vendor = "GenuineIntel", model_name = "Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz", family = 6, march = "Kaby Lake", model = 142, ncpu = 8 } $ xmake l os.cpuinfo march "Kaby Lake" $ xmake l os.cpuinfo ncpu 8 ``` If you want to determine the support of extended features such as `sse`, you need to import the `core.base.cpu` module to get it. ```lua target("test") set_kind("binary") add_files("src/*.c") on_load(function (target) import("core.base.cpu") local ncpu = os.cpuinfo("ncpu") - local ncpu = cpu.number() if cpu.has_feature("sse") then target:add("defines", "HAS_SSE") end end) ``` ### Added cmake import file rules If we are developing a library program, after executing `xmake install` to install to the system, only the library file is installed, and there is no import file information such as .cmake/.pc, so the cmake project wants to be integrated and used through find\_package, usually by looking for Not in our library. In order to allow the third-party cmake project to find it normally and use the integration, then we can use the `utils.install.cmake_importfiles` rule to export the .cmake file when installing the target target library file for the library import and search of other cmake projects . We only need to apply this rule to the specified target library target through the `add_rules` interface. ```lua target("test") set_kind("binary") add_files("src/*.c") add_rules("utils.install.cmake_importfiles") ``` After configuration, the `xmake install` installation command can automatically export the .cmake import file. ### Added pkgconfig import file rules Similar to the `cmake_importfiles` above, but we can also install the pkgconfig/.pc import file through the `utils.install.pkgconfig_importfiles` rule, which is very useful for library detection by tools such as autotools. ```lua target("test") set_kind("binary") add_files("src/*.c") add_rules("utils.install.pkgconfig_importfiles") ``` ### Added Git related built-in configuration variables xmake has always provided the automatic generation feature of config.h, which can be configured through the [add\_configfiles](https://xmake.io/api/description/project-target.html#add-configfiles) interface, and it also supports the replacement of template variables. You can define some variables yourself. However, xmake also provides some commonly used built-in variable substitutions, such as version information, platform architecture, etc. For details, see: The template configuration is very simple, just need: ```lua target("test") set_kind("binary") add_files("src/*.c") add_configfiles("src/config.h.in") ``` The config.h file can be automatically generated according to config.h.in. But the new feature we are going to talk about here is the newly provided Git-related built-in variables to allow users to quickly and conveniently compile the project or the latest tag/branch/commit information of the current git project. This is very useful for troubleshooting and locating problems in the later stage. We can use commit to pinpoint the problem library based on which commit submission caused the problem. In this way, we can checkout but the corresponding version to troubleshoot the problem. We only need to configure and define the following variables in config.h.in. ```c #define GIT_COMMIT "${GIT_COMMIT}" #define GIT_COMMIT_LONG "${GIT_COMMIT_LONG}" #define GIT_COMMIT_DATE "${GIT_COMMIT_DATE}" #define GIT_BRANCH "${GIT_BRANCH}" #define GIT_TAG "${GIT_TAG}" #define GIT_TAG_LONG "${GIT_TAG_LONG}" #define GIT_CUSTOM "${GIT_TAG}-${GIT_COMMIT}" ``` Execute xmake to compile, it will automatically generate the following config.h file. ```c #define GIT_COMMIT "8c42b2c2" #define GIT_COMMIT_LONG "8c42b2c251793861eb85ffdf7e7c2307b129c7ae" #define GIT_COMMIT_DATE "20210121225744" #define GIT_BRANCH "dev" #define GIT_TAG "v1.6.6" #define GIT_TAG_LONG "v1.6.6-0-g8c42b2c2" #define GIT_CUSTOM "v1.6.6-8c42b2c2" ``` We can use them in the program by means of macro definitions. ### Android NDK r22 support and remote pull The Android NDK has made very big structural changes since r22, removing some obsolete directories, such as the top-level sysroot directory and platforms directory, causing the previous detection method of xmake to fail. Therefore, in the new version, we have made improvements to xmake to better support the full version of the NDK toolchain, including the new version above r22. At the same time, the official repository of xmae-repo has also added the inclusion of ndk packages, enabling xmake to pull the ndk tool chain remotely for use. ```lua add_requires("ndk >=22.x") set_toolchains("@ndk", {sdkver = "23"}) target("test") set_kind("binary") add_files("src/*.c") ``` ## Changelog ### New features * [#1259](https://github.com/xmake-io/xmake/issues/1259): Support `add_files("*.def")` to export symbols for windows/dll * [#1267](https://github.com/xmake-io/xmake/issues/1267): add `find_package("nvtx")` * [#1274](https://github.com/xmake-io/xmake/issues/1274): add `platform.linux.bpf` rule to build linux/bpf program * [#1280](https://github.com/xmake-io/xmake/issues/1280): Support fetchonly package to improve find\_package * Support to fetch remote ndk toolchain package * [#1268](https://github.com/xmake-io/xmake/issues/1268): Add `utils.install.pkgconfig_importfiles` rule to install `*.pc` import file * [#1268](https://github.com/xmake-io/xmake/issues/1268): Add `utils.install.cmake_importfiles` rule to install `*.cmake` import files * [#348](https://github.com/xmake-io/xmake-repo/pull/348): Add `platform.longpaths` policy to support git longpaths * [#1314](https://github.com/xmake-io/xmake/issues/1314): Support to install and use conda packages * [#1120](https://github.com/xmake-io/xmake/issues/1120): Add `core.base.cpu` module and improve `os.cpuinfo()` * [#1325](https://github.com/xmake-io/xmake/issues/1325): Add builtin git variables for `add_configfiles` ### Change * [#1275](https://github.com/xmake-io/xmake/issues/1275): Support conditionnal targets for vsxmake plugin * [#1290](https://github.com/xmake-io/xmake/pull/1290): Improve android ndk to support >= r22 * [#1311](https://github.com/xmake-io/xmake/issues/1311): Add packages lib folder to PATH for vsxmake project ### Bugs fixed * [#1266](https://github.com/xmake-io/xmake/issues/1266): Fix relative repo path in `add_repositories` * [#1288](https://github.com/xmake-io/xmake/issues/1288): Fix vsxmake generator with option configs --- --- url: /zh/posts/xmake-update-v2.5.3.md --- 在 2.5.3 版本,我们新增了对 linux bpf 程序的构建支持,并且同时支持 android bpf 程序的构建。 尽管 bpf 对 编译工具链有一定的要求,比如需要较新的 llvm/clang 和 android ndk 工具链,但是 xmake 能够自动拉取特定版本的 llvm/ndk 来用于编译,并且还能自动拉取 libbpf 等依赖库,完全省去了用户折腾编译环境和 libbpf 库集成的问题。 另外,在新版本中我们还新增了对 Conda 包仓库的集成支持,现在除了能够从 Conan/Vcpkg/brew/pacman/clib/dub 等包仓库集成使用包,还能从 Conda 包仓库中集成各种二进制 C/C++ 包。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) * [入门课程](https://xmake.io/zh/) ## 新特性介绍 ### 构建 Linux Bpf 程序 新版本,我们开始支持 bpf 程序构建,同时支持 linux 以及 android 平台,能够自动拉取 llvm 和 android ndk 工具链。 更多详情见:[#1274](https://github.com/xmake-io/xmake/issues/1274) 支持 linux 和 android 两端构建的配置大概如下,如果我们不需要构建 android 版本,可以做一些删减,配置会更加精简: ```lua add_rules("mode.release", "mode.debug") add_rules("platform.linux.bpf") add_requires("linux-tools", {configs = {bpftool = true}}) add_requires("libbpf") if is_plat("android") then add_requires("ndk >=22.x") set_toolchains("@ndk", {sdkver = "23"}) else add_requires("llvm >=10.x") set_toolchains("@llvm") add_requires("linux-headers") end target("minimal") set_kind("binary") add_files("src/*.c") add_packages("linux-tools", "linux-headers", "libbpf") set_license("GPL-2.0") ``` 通过上面的配置,大概可以看出,我们集成配置了特定版本的 llvm 和 NDK 工具链,以及 libbpf, linux-headers, linux-tools 等包,xmake 都会去自动拉取它们,然后使用对应的工具链集成编译这些依赖包,最后生成 bpf 程序。 其中 linux-tools 包主要使用了里面的 libtool 程序,用于生成 bpf skeleton 头文件,xmake 也会自动调用这个工具去生成它。 #### 编译 linux bpf 程序 我们只需要执行 xmake 命令即可完成编译,即使你还没安装 llvm/clang,当然,如果你已经安装了它们,如果版本匹配,xmake 也会去优先使用。 ```bash $ xmake ``` 我们也可以通过 `xmake -v` 来编译并且查看完整详细的编译命令: ```bash $ xmake -v [ 20%]: compiling.bpf src/minimal.bpf.c /usr/bin/ccache /usr/bin/clang -c -Qunused-arguments -m64 -fvisibility=hidden -O3 -Ibuild/.gens/minimal/linux/x86_64/release/rules/bpf -isystem /home/ruki/.xmake/packages/l/linux-tools/5.9.16/0c52e491268946fe9a4bc91d4906d66b/include -isystem /home/ruki/.xmake/packages/z/zlib/1.2.11/3a7e4427eda94fc69fad0009a1629fd8/include -isystem /home/ruki/.xmake/packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include -isystem /home/ruki/.xmake/packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include/libelf -isystem /home/ruki/.xmake/packages/l/libcap/2.27/c55b28aa3b3745489b93895d0d606ed1/include -isystem /home/ruki/.xmake/packages/l/linux-headers/5.9.16/8e3a440cbe1f42249aef3d89f1528ecb/include -DNDEBUG -target bpf -g -o build/.gens/minimal/linux/x86_64/release/rules/bpf/minimal.bpf.o src/minimal.bpf.c llvm-strip -g build/.gens/minimal/linux/x86_64/release/rules/bpf/minimal.bpf.o bpftool gen skeleton build/.gens/minimal/linux/x86_64/release/rules/bpf/minimal.bpf.o [ 40%]: ccache compiling.release src/minimal.c /usr/bin/ccache /usr/bin/clang -c -Qunused-arguments -m64 -fvisibility=hidden -O3 -Ibuild/.gens/minimal/linux/x86_64/release/rules/bpf -isystem /home/ruki/.xmake/packages/l/linux-tools/5.9.16/0c52e491268946fe9a4bc91d4906d66b/include -isystem /home/ruki/.xmake/packages/z/zlib/1.2.11/3a7e4427eda94fc69fad0009a1629fd8/include -isystem /home/ruki/.xmake/packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include -isystem /home/ruki/.xmake/packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/include/libelf -isystem /home/ruki/.xmake/packages/l/libcap/2.27/c55b28aa3b3745489b93895d0d606ed1/include -isystem /home/ruki/.xmake/packages/l/linux-headers/5.9.16/8e3a440cbe1f42249aef3d89f1528ecb/include -DNDEBUG -o build/.objs/minimal/linux/x86_64/release/src/minimal.c.o src/minimal.c [ 60%]: linking.release minimal /usr/bin/clang++ -o build/linux/x86_64/release/minimal build/.objs/minimal/linux/x86_64/release/src/minimal.c.o -m64 -L/home/ruki/.xmake/packages/l/linux-tools/5.9.16/0c52e491268946fe9a4bc91d4906d66b/lib64 -L/home/ruki/.xmake/packages/z/zlib/1.2.11/3a7e4427eda94fc69fad0009a1629fd8/lib -L/home/ruki/.xmake/packages/l/libelf/0.8.13/ced4fdd8151a475dafc5f51e2a031997/lib -L/home/ruki/.xmake/packages/l/libcap/2.27/c55b28aa3b3745489b93895d0d606ed1/lib -s -lbpf -lz -lelf -lcap [100%]: build ok! ``` #### 编译 Android bpf 程序 如果编译 Android 版本,我们也只需要切换到 android 平台即可,一样很方便 ```bash $ xmake f -p android $ xmake ``` xmake 会去自动下来 ndk 工具链和对应 android 版本 libbpf 等库来使用。 ```bash $ xmake f -p android -c checking for architecture ... armeabi-v7a checking for Android SDK directory ... no checking for NDK directory ... no note: try installing these packages (pass -y to skip confirm)? in local-repo: -> libcap 2.27 [linux, x86_64, from:linux-tools] -> libelf 0.8.13 [linux, x86_64, from:linux-tools] -> zlib 1.2.11 [linux, x86_64, from:linux-tools] -> linux-tools 5.9.16 [bpftool:y] -> ndk 22.0 -> libelf#1 0.8.13 [toolchains:@ndk, from:libbpf] -> zlib#1 1.2.11 [toolchains:@ndk, from:libbpf] -> libbpf v0.3 [toolchains:@ndk] please input: y (y/n) => install libcap 2.27 .. ok => install zlib 1.2.11 .. ok => install libelf 0.8.13 .. ok => install ndk 22.0 .. ok => install linux-tools 5.9.16 .. ok => install libelf#1 0.8.13 .. ok => install zlib#1 1.2.11 .. ok => install libbpf v0.3 .. ok ruki@010689392c4d:/mnt/bpf_minimal$ xmake [ 20%]: compiling.bpf src/minimal.bpf.c [ 40%]: ccache compiling.release src/minimal.c [ 60%]: linking.release minimal [100%]: build ok! ``` 当然,如果你已经手动下载了对应版本的 ndk 工具链,我们也可以指定使用,不再走自动拉取。 ```bash $ xmake f -p android --ndk=/xxx/android-ndk-r22 $ xmake ``` 不过,如果自己下载的话,记得至少要下载 ndk r22 以上版本的,因为低版本 ndk 里面的 clang 不支持编译生成 bpf 程序。 最后,这里有个完整的基于 xmake 的 bpf 脚手架工程,大家可以参考下: 另外这里也有个最小 bpf 例子程序:https://github.com/xmake-io/xmake/tree/master/tests/projects/bpf/minimal ### 集成使用 Conda 包 Conda 是一个很强大的第三方包管理器,支持各种语言的二进制包拉取,这里我们仅仅使用里面的 C/C++ 包。 它的集成使用方式跟 conan/vcpkg 类似,仅仅只是包命名空间改成了 `conda::` ```lua add_requires("conda::libpng 1.6.37", {alias = "libpng"}) add_requires("conda::openssl") target("testco") set_kind("binary") add_files("src/*.cpp") add_packages("libpng", "conda::openssl") ``` 注:虽然我们支持很多的第三方包管理器,比如 conan/conda/vcpkg/brew 等等,但是 xmake 也有自建的包仓库管理,目前已有将近 300 个常用包,支持不同的平台,其中部分包还支持 android/ios/mingw 甚至交叉编译环境。 因此,如果官方 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库已经提供了需要的包,可以直接使用,不需要指定包命名空间。xmake 对第三方包管理的支持仅仅是作为补充,尽可能复用已有 c/c++ 生态,避免生态碎片化。 ### 获取主机 cpu 信息 当前版本,我们新增了一个 `core.base.cpu` 模块和 `os.cpuinfo` 接口,用于获取 cpu 的各种信息,比如:cpu family/model, microarchitecture,core number, features 等信息。 这通常在追求性能的项目中非常有用,这些项目通常需要根据CPU的内存模型和扩展指令集来优化,同时想要跨平台的话,就需要根据当前cpu信息指定对应的代码,(例如intel haswell之后一套,amd zen之后一套,更老的默认到没有优化)。很多高性能计算库里面也会用到这些信息。 因此,通过这个模块接口就可以在编译配置的时候获取当前主机 cpu 的信息和特性支持力度,来针对性开启相关优化编译。 我们可以通过 `os.cpuinfo()` 快速获取所有信息,也可以指定 `os.cpuinfo("march")` 获取特定信息,比如 march,也就是 microarchitecture 我们也可以通过 `xmake l` 命令来快速查看获取结果。 ```bash $ xmake l os.cpuinfo { features = "fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clfsh ds acpi mmx fxsr sse sse2 ss htt tm pbe sse3 pclmulqdq dtes64 mon dscpl vmx est tm2 ssse3 fma cx16 tpr pdcm sse4_1 sse4_2 x2apic movbe popcnt aes pcid xsave osxsave seglim64 tsctmr avx1_0 rdrand f16c", vendor = "GenuineIntel", model_name = "Intel(R) Core(TM) i7-8569U CPU @ 2.80GHz", family = 6, march = "Kaby Lake", model = 142, ncpu = 8 } $ xmake l os.cpuinfo march "Kaby Lake" $ xmake l os.cpuinfo ncpu 8 ``` 如果要判断 sse 等扩展特性支持,就得需要 import 导入 `core.base.cpu` 模块来获取了。 ```lua target("test") set_kind("binary") add_files("src/*.c") on_load(function (target) import("core.base.cpu") local ncpu = os.cpuinfo("ncpu") -- local ncpu = cpu.number() if cpu.has_feature("sse") then target:add("defines", "HAS_SSE") end end) ``` ### 新增 cmake 导入文件规则 如果,我们开发的是库程序,在执行 `xmake install` 安装到系统后,仅仅只安装了库文件,没有 .cmake/.pc 等导入文件信息,因此 cmake 工程想通过 find\_package 集成使用,通常是找不到我们的库。 为了能够让第三方 cmake 工程正常找到它并使用集成,那么我们可以使用 `utils.install.cmake_importfiles` 规则在安装 target 目标库文件的时候,导出 .cmake 文件,用于其他 cmake 项目的库导入和查找。 我们只需要通过 add\_rules 接口应用此规则到指定的 target 库目标即可。 ```lua target("test") set_kind("binary") add_files("src/*.c") add_rules("utils.install.cmake_importfiles") ``` 配置之后,`xmake install` 安装命令就能够自动导出 .cmake 导入文件。 ### 新增 pkgconfig 导入文件规则 跟上面的 cmake 导入类似,只不过我们这也可以通过 `utils.install.pkgconfig_importfiles` 规则安装 pkgconfig/.pc 导入文件,这对 autotools 等工具的库探测非常有用。 ```lua target("test") set_kind("binary") add_files("src/*.c") add_rules("utils.install.pkgconfig_importfiles") ``` ### 新增 Git 相关内置配置变量 xmake 一直有提供 config.h 的自动生成特性,可以通过 [add\_configfiles](https://xmake.io/zh/) 接口来配置,并且它还支持模板变量的替换,用户可以自己定义一些变量。 不过,xmake 也提供了一些常用的内置变量替换,比如 版本信息,平台架构等。具体详情见:[/zh/api/description/project-target#add-configfiles](https://xmake.io/zh/) 模板配置使用方式很简单,只需要: ```lua target("test") set_kind("binary") add_files("src/*.c") add_configfiles("src/config.h.in") ``` 就能根据 config.h.in 自动生成 config.h 文件。 不过我们这里要讲的新特性是最近新提供的 Git 相关内置变量,来让用户在项目编译时候,快速方便的或者当前 git 项目最近的 tag/branch/commit 信息。 这对于后期排查定位问题非常有用,我们可以通过 commit 精确定位问题库是基于哪次 commit 提交导致的,这样,我们就能 checkout 但对应版本来排查问题。 我们只需要在 config.h.in 中配置定义以下变量。 ```c #define GIT_COMMIT "${GIT_COMMIT}" #define GIT_COMMIT_LONG "${GIT_COMMIT_LONG}" #define GIT_COMMIT_DATE "${GIT_COMMIT_DATE}" #define GIT_BRANCH "${GIT_BRANCH}" #define GIT_TAG "${GIT_TAG}" #define GIT_TAG_LONG "${GIT_TAG_LONG}" #define GIT_CUSTOM "${GIT_TAG}-${GIT_COMMIT}" ``` 执行 xmake 编译,就会自动生成如下 config.h 文件。 ```c #define GIT_COMMIT "8c42b2c2" #define GIT_COMMIT_LONG "8c42b2c251793861eb85ffdf7e7c2307b129c7ae" #define GIT_COMMIT_DATE "20210121225744" #define GIT_BRANCH "dev" #define GIT_TAG "v1.6.6" #define GIT_TAG_LONG "v1.6.6-0-g8c42b2c2" #define GIT_CUSTOM "v1.6.6-8c42b2c2" ``` 我们就可以在程序用通过宏定义的方式使用它们。 ### Android NDK r22 支持和远程拉取 Android NDK 从 r22 之后,结构上做了非常大的改动,移除了一些被废弃的目录,比如 顶层的 sysroot 目录 和 platforms 目录,导致 xmake 之前的探测方式失效。 因此在新版本中,我们对 xmake 做了改进来更好的支持全版本 NDK 工具链,包括 r22 以上的新版本。 同时 xmae-repo 官方仓库也增加了对 ndk 包的收录,使得 xmake 能够远程拉取 ndk 工具链来使用。 ```lua add_requires("ndk >=22.x") set_toolchains("@ndk", {sdkver = "23"}) target("test") set_kind("binary") add_files("src/*.c") ``` ## 更新内容 ### 新特性 * [#1259](https://github.com/xmake-io/xmake/issues/1259): 支持 `add_files("*.def")` 添加 def 文件去导出 windows/dll 符号 * [#1267](https://github.com/xmake-io/xmake/issues/1267): 添加 `find_package("nvtx")` * [#1274](https://github.com/xmake-io/xmake/issues/1274): 添加 `platform.linux.bpf` 规则去构建 linux/bpf 程序 * [#1280](https://github.com/xmake-io/xmake/issues/1280): 支持 fetchonly 包去扩展改进 find\_package * 支持自动拉取远程 ndk 工具链包和集成 * [#1268](https://github.com/xmake-io/xmake/issues/1268): 添加 `utils.install.pkgconfig_importfiles` 规则去安装 `*.pc` 文件 * [#1268](https://github.com/xmake-io/xmake/issues/1268): 添加 `utils.install.cmake_importfiles` 规则去安装 `*.cmake` 导入文件 * [#348](https://github.com/xmake-io/xmake-repo/pull/348): 添加 `platform.longpaths` 策略去支持 git longpaths * [#1314](https://github.com/xmake-io/xmake/issues/1314): 支持安装使用 conda 包 * [#1120](https://github.com/xmake-io/xmake/issues/1120): 添加 `core.base.cpu` 模块并且改进 `os.cpuinfo()` * [#1325](https://github.com/xmake-io/xmake/issues/1325): 为 `add_configfiles` 添加内建的 git 变量 ### 改进 * [#1275](https://github.com/xmake-io/xmake/issues/1275): 改进 vsxmake 生成器,支持条件化编译 targets * [#1290](https://github.com/xmake-io/xmake/pull/1290): 增加对 Android ndk r22 以上版本支持 * [#1311](https://github.com/xmake-io/xmake/issues/1311): 为 vsxmake 工程添加包 dll 路径,确保调试运行加载正常 ### Bugs 修复 * [#1266](https://github.com/xmake-io/xmake/issues/1266): 修复在 `add_repositories` 中的 repo 相对路径 * [#1288](https://github.com/xmake-io/xmake/issues/1288): 修复 vsxmake 插件处理 option 配置问题 --- --- url: /posts/xmake-update-v2.5.4.md --- ### New package manager support #### Add dependency package of ubuntu/apt Now we support the use of apt to integrate dependent packages, and will also automatically find packages that have been installed on the ubuntu system. ```lua add_requires("apt::zlib1g-dev", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` #### Add gentoo/portage dependency package We also support the use of Portage to integrate dependency packages, and will automatically find packages already installed on the Gentoo system. ```lua add_requires("portage::libhandy", {alias = "libhandy"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("libhandy") ``` #### Integrate arm/arm64 architecture package from Vcpkg ```lua add_requires("vcpkg::zlib", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` The configuration method is still the same as before. You only need to switch to the arm/arm64 architecture to compile to automatically pull the arm/arm64 package from Vcpkg. ```console $ xmake f -a arm64 $ xmake ``` #### Support import and export installation packages Usually, after we use the xrepo command or xmake to install the package, if the same project is migrated to other machines for compilation, then the installation package must be downloaded again. In order to improve development efficiency, xrepo can now quickly export installed packages, including corresponding library files, header files, and so on. ```console $ xrepo export -o /tmp/output zlib ``` Then we can also import the previously exported installation package on other machines to implement package migration. ```console $ xrepo import -i /xxx/packagedir zlib ``` After importing, the corresponding project compilation will use them directly, no additional reinstallation of packages is required. #### Specific package shell environment support xrepo has a `xrepo env` command, which can specify the environment to load a specific package, and then run a specific program, for example, load the installation environment of the luajit package, and then run luajit: ```console $ xrepo env luajit ``` Or bind a specific luajit version package environment, after loading bash, you can directly run the corresponding lujit. ```console $ xrepo env -b "luajit 5.1" bash > luajit --version ``` However, there is a problem with this. If we install a lot of packages, different package configurations and versions are still different. If we want to load a bash environment with multiple packages at the same time. Then, the previous method cannot be supported. Therefore, in the new version, we will further improve it. Yes, we can customize some package configurations by adding the xmake.lua file in the current directory, and then enter the specific package shell environment . xmake.lua ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` For example, as above, we have configured three packages in xmake.lua and want to use them in the shell at the same time, then just run the following command in the current directory. ```console $ xrepo env shell > python --version > luajit --version ``` It should be noted that here we used `xrepo env shell` instead of `xrepo env bash`, because bash can only be used on specific platforms, and `xrepo env shell` is a built-in command. It can automatically detect the current terminal environment, load the corresponding bash, sh, zsh, and cmd or powershell environments under windows, all of which are automatic. In addition, we also added some auxiliary features, such as prompt prompt, `xrepo env quit` environment exit command, historical input command switching and so on. #### Set up image acceleration package download In order to improve the problem of slow downloading of packages in the domestic network environment, xmake supports proxy settings, as well as pac.lua proxy configuration strategies. In the new version, we have improved the configuration of pac.lua and further support the configuration of mirror proxy rules. For example, access to all github.com domain names is switched to the hub.fastgit.org domain name to achieve accelerated downloading of packages. pac.lua configuration: ```lua function mirror(url) return url:gsub("github.com", "hub.fastgit.org") end ``` Then we set the second pac.lua file, the default path is `~/.xmake/pac.lua`. ```console $ xmake g --proxy_pac=/tmp/pac.lua ``` Then, when we install the package, if we encounter the package source under the github.com domain name, it will automatically switch to the fastgit mirror to accelerate the download when downloading. ```console $ xrepo install libpng > curl https://hub.fastgit.org/glennrp/libpng/archive/v1.6.37.zip -o v1.6.37.zip ``` #### Custom switch package storage directory Before, we could only configure and modify the default package installation directory through `xmake g --pkg_installdir=/tmp/xx`. Now, we can also modify it through the `XMAKE_PKG_INSTALLDIR` environment variable. The default path is: `~/.xmake/packages`. In addition, we also added the `XMAKE_PKG_CACHEDIR` environment variable to modify the package cache directory. The default path is: `~/.xmake/cache/packages`. ## Changelog ### New features * [#1323](https://github.com/xmake-io/xmake/issues/1323): Support find and install package from `apt`, `add_requires("apt::zlib1g-dev")` * [#1337](https://github.com/xmake-io/xmake/issues/1337): Add environment vars to change package directories * [#1338](https://github.com/xmake-io/xmake/issues/1338): Support import and export installed packages * [#1087](https://github.com/xmake-io/xmake/issues/1087): Add `xrepo env shell` and support load envs from `add_requires/xmake.lua` * [#1313](https://github.com/xmake-io/xmake/issues/1313): Support private package for `add_requires/add_deps` * [#1358](https://github.com/xmake-io/xmake/issues/1358): Support to set mirror url to speedup download package * [#1369](https://github.com/xmake-io/xmake/pull/1369): Support arm/arm64 packages for vcpkg, thanks @fallending * [#1405](https://github.com/xmake-io/xmake/pull/1405): Add portage package manager support, thanks @Phate6660 ### Change * Improve `find_package` and add `package:find_package` for xmake package * Remove deprecated `set_config_h` and `set_config_h_prefix` apis * [#1343](https://github.com/xmake-io/xmake/issues/1343): Improve to search local package files * [#1347](https://github.com/xmake-io/xmake/issues/1347): Improve to vs\_runtime configs for binary package * [#1353](https://github.com/xmake-io/xmake/issues/1353): Improve del\_files() to speedup matching files * [#1349](https://github.com/xmake-io/xmake/issues/1349): Improve `xrepo env shell` to support powershell ### Bugs fixed * [#1380](https://github.com/xmake-io/xmake/issues/1380): Fix add packages errors * [#1381](https://github.com/xmake-io/xmake/issues/1381): Fix add local git source for package * [#1391](https://github.com/xmake-io/xmake/issues/1391): Fix cuda/nvcc toolchain --- --- url: /zh/posts/xmake-update-v2.5.4.md --- 在 2.5.4 版本中,我们新增了对 Apt、Portage 这两个包管理器的支持,在 Ubuntu/Gentoo 上我们也可以使用 `add_requires` 可以快速集成它们提供的包。 并且我们也改进支持了 Vcpkg 包管理器的支持,新增对 arm/arm64 架构包的安装支持。 另外,我们还增强了 `xrepo env shell` 环境,可以通过在 `xmake.lua` 中配置一系列 `add_requires` 包配置,加载带有特定包配置的 shell 环境。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) * [入门课程](https://xmake.io/zh/) ## 新特性介绍 ### 新的包管理器支持 #### 添加 ubuntu/apt 的依赖包 现在我们支持使用 apt 集成依赖包,也会自动查找 ubuntu 系统上已经安装的包。 ```lua add_requires("apt::zlib1g-dev", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` #### 添加 gentoo/portage 的依赖包 我们也支持了使用 Portage 集成依赖包,并且也会自动查找 Gentoo 系统上已经安装的包。 ```lua add_requires("portage::libhandy", {alias = "libhandy"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("libhandy") ``` #### 从 Vcpkg 集成 arm/arm64 架构包 ```lua add_requires("vcpkg::zlib", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` 配置方式还是跟之前的相同,只需要切换到 arm/arm64 架构编译就可以自动从 Vcpkg 拉取 arm/arm64 的包。 ```console $ xmake f -a arm64 $ xmake ``` #### 支持导入导出安装包 通常,我们使用 xrepo 命令或者 xmake 去安装完包后,如果相同的项目迁移到其他机器编译,那就要重新下载安装包。 为了提高开发效率,现在 xrepo 可以快速导出已经安装后的包,包括对应的库文件,头文件等等。 ```console $ xrepo export -o /tmp/output zlib ``` 然后我们也可以在其他机器上导入之前导出的安装包,实现包的迁移。 ```console $ xrepo import -i /xxx/packagedir zlib ``` 导入后,对应项目编译会直接使用它们,不再额外重新安装包。 #### 特定包 shell 环境支持 xrepo 有个 `xrepo env` 命令,可以指定加载特定包的环境,然后运行特定程序,例如加载 luajit 包的安装环境,然后运行 luajit: ```console $ xrepo env luajit ``` 或者绑定特定 luajit 版本包环境,加载 bash 后,就可以直接运行对应的 lujit。 ```console $ xrepo env -b "luajit 5.1" bash > luajit --version ``` 但是,这样有个问题,如果我们安装的包很多,不同的包配置和版本都还不同,如果我们想加载一个 bash,并且同时带有多个包的环境。 那么,之前的方式就无法支持了,因此,新版本中,我们对其进一步改进,是的可以通过在当前目录下,添加 xmake.lua 文件,定制化一些包配置,然后进入特定的包 shell 环境。 xmake.lua ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` 比如上面这样,我们通过在 xmake.lua 中配置了三个包,想在 shell 中同时使用它们,那么只需要在当前目录下运行下面的命令就行了。 ```console $ xrepo env shell > python --version > luajit --version ``` 需要注意的是,这里我们使用了 `xrepo env shell` 而不是 `xrepo env bash`,是因为 bash 只能在特定平台使用,而 `xrepo env shell` 属于内置命令。 它可以自动检测当前用的终端环境,加载对应的 bash, sh, zsh 以及 windows 下的 cmd 或者 powershell 环境,这一切都是自动的。 另外,我们还加了一些辅助特性,比如 prompt 提示,`xrepo env quit` 环境退出命令,历史输入命令切换等等。 #### 设置镜像加速包下载 为了改进国内网络环境下载包慢的问题,xmake 是支持代理设置的,还可以支持 pac.lua 代理配置策略。 而新版本中,我们对 pac.lua 配置进行了改进,进一步支持配置镜像代理规则,比如对所有 github.com 域名的访问切到 hub.fastgit.org 域名,实现加速下载包。 pac.lua 配置: ```lua function mirror(url) return url:gsub("github.com", "hub.fastgit.org") end ``` 然后我们设置次 pac.lua 文件,默认路径在 `~/.xmake/pac.lua`。 ```console $ xmake g --proxy_pac=/tmp/pac.lua ``` 然后,我们安装包的时候,如果遇到 github.com 域名下的包源,下载时候会自动切到 fastgit 镜像加速下载。 ```console $ xrepo install libpng > curl https://hub.fastgit.org/glennrp/libpng/archive/v1.6.37.zip -o v1.6.37.zip ``` #### 自定义切换包存储目录 之前我们只能通过 `xmake g --pkg_installdir=/tmp/xx` 来配置修改默认的包安装目录。 现在,我们也可以通过 `XMAKE_PKG_INSTALLDIR` 环境变量也修改它,默认路径在:`~/.xmake/packages`。 另外,我们还额外添加了 `XMAKE_PKG_CACHEDIR` 环境变量来修改包的缓存目录,默认路径在:`~/.xmake/cache/packages`。 ## 更新内容 ### 新特性 * [#1323](https://github.com/xmake-io/xmake/issues/1323): 支持从 apt 查找安装包,`add_requires("apt::zlib1g-dev")` * [#1337](https://github.com/xmake-io/xmake/issues/1337): 添加环境变量去改进包安装和缓存目录 * [#1338](https://github.com/xmake-io/xmake/issues/1338): 支持导入导出已安装的包 * [#1087](https://github.com/xmake-io/xmake/issues/1087): 添加 `xrepo env shell` 并且支持从 `add_requires/xmake.lua` 加载包环境 * [#1313](https://github.com/xmake-io/xmake/issues/1313): 为 `add_requires/add_deps` 添加私有包支持 * [#1358](https://github.com/xmake-io/xmake/issues/1358): 支持设置镜像 url 站点加速包下载 * [#1369](https://github.com/xmake-io/xmake/pull/1369): 为 vcpkg 增加 arm/arm64 包集成支持,感谢 @fallending * [#1405](https://github.com/xmake-io/xmake/pull/1405): 添加 portage 包管理器支持,感谢 @Phate6660 ### 改进 * 改进 `find_package` 并且添加 `package:find_package` 接口在包定义中方便查找包 * 移除废弃的 `set_config_h` 和 `set_config_h_prefix` 接口 * [#1343](https://github.com/xmake-io/xmake/issues/1343): 改进搜索本地包文件 * [#1347](https://github.com/xmake-io/xmake/issues/1347): 针对 binary 包改进 vs\_runtime 配置 * [#1353](https://github.com/xmake-io/xmake/issues/1353): 改进 del\_files() 去加速匹配文件 * [#1349](https://github.com/xmake-io/xmake/issues/1349): 改进 xrepo env shell 支持,更好的支持 powershell ### Bugs 修复 * [#1380](https://github.com/xmake-io/xmake/issues/1380): 修复 `add_packages()` 失败问题 * [#1381](https://github.com/xmake-io/xmake/issues/1381): 修复添加本地 git 包源问题 * [#1391](https://github.com/xmake-io/xmake/issues/1391): 修复 cuda/nvcc 工具链 --- --- url: /posts/xmake-update-v2.5.5.md --- ### Download and install the pre-compiled package Each time you install a package by the built-in package manager of xmake, you must download the corresponding package source code, and then perform local compilation and installation integration. This is for some large packages that compile very slowly, and some packages that rely on a lot of compilation tools. It will be very slow. Especially on windows, not only the dependence of the third party package on the compilation environment is more complicated, but also many packages and compilation are very slow, such as boost, openssl and so on. To this end, we implement cloud pre-compilation of packages based on github action, and pre-compile all commonly used packages, and then store them in \[build-artifacts]\(https://github.com/xmake-mirror/build- artifacts) under Releases of the repository. Then, when we install the package, we will automatically download it from the binary image package source to achieve rapid integration (currently only pre-compiled windows packages are supported, and will be gradually released to other platforms in the future). We will pre-compile various configuration combinations such as plat/arch/MT/MD/static/shared of each package, and accurately pull the packages that users actually need according to the unique buildhash. All compiled products will be compressed and packaged with 7zip, as follows Picture: ![](/assets/img/posts/xmake/build-artifacts.png) #### Configure mirror source to accelerate download Since our pre-compiled products are all placed on github, for Chinese users, considering that access to github is not very stable, we can also use the xmake mirror proxy function to automatically switch the actual download to fastgit and other mirror sites to speed up the download. We can configure mirror proxy rules through a pac.lua file, for example, access to all github.com domain names is switched to the hub.fastgit.org domain name to speed up downloading packages. pac.lua configuration: ```lua function mirror(url) return url:gsub("github.com", "hub.fastgit.org") end ``` Then we set this pac.lua file, the default path is `~/.xmake/pac.lua`, or you can manually configure pac.lua using the specified location. ```console $ xmake g --proxy_pac=/tmp/pac.lua ``` Then, when we install the package, if we encounter the package source under the github.com domain name, it will automatically switch to the fastgit mirror to accelerate the download when downloading. ```console $ xrepo install libpng > curl https://hub.fastgit.org/glennrp/libpng/archive/v1.6.37.zip -o v1.6.37.zip ``` Therefore, all downloads of pre-compiled products will also be speeded up. Of course, more than Fastgit provides github mirror acceleration in China, and users can also switch to other mirror sources, such as cnpmjs.org and so on. #### How to trigger cloud pre-compilation By default, xmake will not actively perform cloud pre-compilation and caching of all packages, which is too time-consuming and labor-intensive. Currently, only the pr is submitted to \[xmake-repo]\(https://github.com/xmake-io/xmake- repo) The official package repository, when a new package is included or the package version is updated, the cloud pre-compilation behavior of the corresponding package will be automatically triggered. Therefore, if users want to contribute packages to our warehouse, they can basically be precompiled and cached (except for headeronly libraries), and if users do not want to contribute packages, but also want to get the precompilation acceleration of the corresponding package, it is also possible. Just submit pr to the build branch of the [build-artifacts](https://github.com/xmake-mirror/build-artifacts) repository, edit \[build.txt]\(https://github.com/xmake-mirror /build-artifacts/blob/build/build.txt) file, just modify the package name and version list that needs to trigger pre-compilation, for example: build.txt ```lua { name = "asmjit", versions = { "2021.06.27" } } ``` As long as pr is merged, it will automatically trigger the pre-compilation behavior, and then generate the final compilation product to releases. #### Mandatory source code compilation and installation Although we provide a pre-compilation, download and installation method, if users still want to compile and install from source code, we can also manually pass in the `--build` parameter to the xrepo command to force switch to source code compilation and installation mode. ```console $ xrepo install --build openssl ``` In xmake.lua, we can also support source code compilation and installation. ```lua add_requires("openssl", {build = true}) ``` If it is not specified, then xmake will automatically try to download and install the precompiled package first. #### Add a private pre-compiled package warehouse Our official pre-compiled package repository is at: [build-artifacts](https://github.com/xmake-mirror/build-artifacts). Similarly, we can also configure and add our own pre-compiled warehouse, the way to add is similar: ```console $ xmake repo --add local-repo git@github.com:xmake-mirror/myrepo-artifacts.git ``` You can also add in xmake.lua: ```lua add_repositories("local-repo git@github.com:xmake-mirror/myrepo-artifacts.git") ``` ### New version of the land package plan #### Default packaging format In the new version, we provide a new local package packaging solution that will seamlessly connect `add_requires` and `add_packages`. We can execute the `xmake package` command to generate the default new version of the packaging format. ```console $ xmake package package(foo): build/packages/f/foo generated ``` It will generate the file `build/packages/f/foo/xmake.lua` with the following content: ```lua package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") on_load(function (package) package:set("installdir", path.join(os.scriptdir(), package:plat(), package:arch(), package:mode())) end) on_fetch(function (package) local result = {} result.links = "foo" result.linkdirs = package:installdir("lib") result.includedirs = package:installdir("include") return result end) ``` In fact, it uses `package()` to define and describe local packages, just like remote packages. The generated directory structure is as follows: ```console $ tree build/packages/f/foo/ build/packages/f/foo/ ├── macosx │ └── x86_64 │ └── release │ ├── include │ │ └── foo.h │ └── lib │ └── libfoo.a └── xmake.lua ``` We can also use the `add_requires`/`add_repositories` interface to seamlessly integrate this package. ```lua add_rules("mode.debug", "mode.release") add_repositories("local-repo build") add_requires("foo") target("bar") set_kind("binary") add_files("src/*.cpp") add_packages("foo") ``` Among them, the `add_repositories` configuration specifies the warehouse root directory of the local package, and then this package can be referenced through `add_requires`. In addition, the generated local package has another feature, which is to support `target/add_deps`, which automatically associates the dependencies of multiple packages, and automatically connects all dependency links during integration. Here is the complete [test example](https://github.com/xmake-io/xmake/blob/dev/tests/actions/package/localpkg/test.lua). ```console "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/bar build/.objs/bar/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version -min=10.15 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk -stdlib=libc++ -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/f/foo/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/s/sub/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/a/add/macosx/x86_64/release/lib -Wl,-x -lfoo -lsub -ladd -lz ``` Note: The previous old version of the local packaging format is an early product and will still be retained, but it is not recommended to continue to use. If you want to continue to use it, you can execute the following command to package: ```console $ xmake package -f oldpkg ``` #### Generate remote package In addition to the local package format, `xmake package` now also supports generating remote packages, so that users can quickly submit them to remote warehouses. We only need to modify the package format when packaging. ```console $ xmake package -f remote ``` He will also generate packages/f/foo/xmake.lua file. ```lua package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") add_urls("https://github.com/myrepo/foo.git") add_versions("1.0", "") on_install(function (package) local configs = {} if package:config("shared") then configs.kind = "shared" end import("package.tools.xmake").install(package, configs) end) on_test(function (package) - TODO check includes and interfaces - assert(package:has_cfuncs("foo", {includes = "foo.h"}) end) ``` Compared with the local package, the package definition configuration has more actual installation logic, as well as the settings of urls and versions, We can also modify urls, versions and other configuration values through additional parameters, for example: ```console $ xmake package -f remote --url=https://xxxx/xxx.tar.gz --shasum=xxxxx --homepage=xxxxx` ``` xmake will also read the relevant configuration information from the target's `set_license` and `set_version` configurations. ### Search for packages from third-party warehouses The built-in xrepo package manager command of xmake previously supported searching the built-in packages in the xmake-repo repository. ```console $ xrepo search zlib "pcr*" zlib: -> zlib: A Massively Spiffy Yet Delicately Unobtrusive Compression Library (in xmake-repo) pcr*: -> pcre2: A Perl Compatible Regular Expressions Library (in xmake-repo) -> pcre: A Perl Compatible Regular Expressions Library (in xmake-repo) ``` And now, we can also search for their packages from third-party package managers such as vcpkg, conan, conda and apt, just add the corresponding package namespace, for example: ```console $ xrepo search vcpkg::pcre The package names: vcpkg::pcre: -> vcpkg::pcre-8.44#8: Perl Compatible Regular Expressions -> vcpkg::pcre2-10.35#2: PCRE2 is a re-working of the original Perl Compatible Regular Expressions library ``` ```console $ xrepo search conan::openssl The package names: conan::openssl: -> conan::openssl/1.1.1g: -> conan::openssl/1.1.1h: ``` ### Modify the target file name We know that for the modification of the target file name, we can use `set_basename` or use the `set_filename` interface to configure the implementation. The former modifies the name of the `xxx` part of `libxxx.so` and the latter can modify the complete file name. But in some cases, we just want to modify: extension `.so`, prefix name `lib`, or adding suffix name such as: `libxxx-d.so` will be very troublesome, or use `set_filename` for complete modification. Now, we newly provide three independent interfaces `set_prefixname`, `set_suffixname` and `set_extension` to configure them more flexibly. #### Set the leading name of the target file For example, change the default: `libtest.so` to `test.so` ```lua target("test") set_prefixname("") ``` #### Set the postname of the target file For example, change the default: `libtest.so` to `libtest-d.so` ```lua target("test") set_suffixname("-d") ``` #### Set the extension of the target file For example, change the default: `libtest.so` to `test.dll` ```lua target("test") set_prefixname("") set_extension(".dll") ``` ### Default target type In the new version, if the user does not specify the target type in the target setting `set_kind`, then the default is the binary program. Therefore, we can achieve smaller configurations, such as: ```lua target("test") add_files("src/*.c") ``` Compilation of some small projects can be completed in just two lines, or even shorter: ```lua target("test", {files = "src/*.c"}) ``` ### New appletvos compilation platform We have also added a new appletvos compilation platform to support the compilation of programs on Apple's TVOS system. All you need is: ```console $ xmake f -p appletvos $ xmake ``` ### Import and export compile configuration We can also import and export the configured configuration set to facilitate the rapid migration of the configuration. #### Export configuration ```console $ xmake f --export=/tmp/config.txt $ xmake f -m debug --xxx=y --export=/tmp/config.txt ``` #### Import configuration ```console $ xmake f --import=/tmp/config.txt $ xmake f -m debug --xxx=y --import=/tmp/config.txt ``` #### Export configuration (with menu) ```console $ xmake f --menu --export=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --export=/tmp/config.txt ``` #### Import configuration (with menu) ```console $ xmake f --menu --import=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --import=/tmp/config.txt ``` ### vs2022 support In addition, in the new version, we have also added support for the preview version of vs2020. ### Improve xrepo shell environment In the last version, we supported customizing some package configurations by adding the xmake.lua file in the current directory, and then entering a specific package shell environment. ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```console $ xrepo env shell > python --version > luajit --version ``` And now, we can also configure and load the corresponding toolchain environment in xmake.lua, for example, load the VS compilation environment. ```lua set_toolchains("msvc") ``` ## Changelog ### New features * [#1421](https://github.com/xmake-io/xmake/issues/1421): Add prefix, suffix and extension options for target names * [#1422](https://github.com/xmake-io/xmake/issues/1422): Support search packages from vcpkg, conan * [#1424](https://github.com/xmake-io/xmake/issues/1424): Set binary as default target kind * [#1140](https://github.com/xmake-io/xmake/issues/1140): Add a way to ask xmake to try to download dependencies from a certain package manager * [#1339](https://github.com/xmake-io/xmake/issues/1339): Improve `xmake package` to generate new local/remote packages * Add `appletvos` platform support for AppleTV, `xmake f -p appletvos` * [#1437](https://github.com/xmake-io/xmake/issues/1437): Add headeronly library type for package to ignore `vs_runtime` * [#1351](https://github.com/xmake-io/xmake/issues/1351): Support export/import current configs * [#1454](https://github.com/xmake-io/xmake/issues/1454): Support to download and install precompiled image packages from xmake-mirror ### Change * [#1425](https://github.com/xmake-io/xmake/issues/1425): Improve tools/meson to load msvc envirnoments * [#1442](https://github.com/xmake-io/xmake/issues/1442): Support to clone package resources from git url * [#1389](https://github.com/xmake-io/xmake/issues/1389): Support to add toolchain envs to `xrepo env` * [#1453](https://github.com/xmake-io/xmake/issues/1453): Support to export protobuf includedirs * Support vs2022 ### Bugs fixed * [#1413](https://github.com/xmake-io/xmake/issues/1413): Fix hangs on fetching packages * [#1420](https://github.com/xmake-io/xmake/issues/1420): Fix config and packages cache * [#1445](https://github.com/xmake-io/xmake/issues/1445): Fix WDK driver sign error * [#1465](https://github.com/xmake-io/xmake/issues/1465): Fix missing link directory --- --- url: /zh/posts/xmake-update-v2.5.5.md --- 2.5.5 版本中,我们继续改进远程包集成的体验,实现在云端预编译包,然后直接下载集成预编译的二进制包。这对于一些编译非常慢的包,可以极大的减少包的安装时间。 另外,新版本中,我们还重新实现了新版的本地包生成方案,完全无缝支持 `add_requires` 和 `add_packages`,从此远程包和本地包可以使用统一的集成方式来维护。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) * [入门课程](https://xmake.io/zh/) ## 新特性介绍 ### 下载安装预编译包 之前 xmake 内置的包管理器每次安装包,都必须下载对应的包源码,然后执行本地编译安装集成,这对于一些编译非常慢的大包,以及一些依赖的编译工具非常多的包,安装起来会非常的慢。 尤其是在 windows 上,不仅三方包对编译环境的依赖更加复杂,而且很多打包编译非常慢,例如:boost, openssl 等等。 为此,我们基于 github action 实现对包的云端预编译,会将常用配置的包都去预编译一遍,然后存储到 [build-artifacts](https://github.com/xmake-mirror/build-artifacts) 仓库的 Releases 下。 然后,我们在安装包的时候,会自动从二进制镜像包源下载,实现快速集成(目前仅支持预编译 windows 包,后期会逐步放开到其他平台)。 我们会预编译每个包的 plat/arch/MT/MD/static/shared 等各种配置组合,根据唯一的 buildhash 来精确拉取用户实际需要的包,所有的编译产物都会用 7zip 压缩打包,如下图: ![](/assets/img/posts/xmake/build-artifacts.png) #### 配置镜像源加速下载 由于我们的预编译产物都放置在 github 上,对于国内用户,考虑到访问 github 并不是很稳定,我们也可以借助 xmake 镜像代理功能,将实际的下载自动切换到 fastgit 等镜像站点加速下载。 我们可以通过一个 pac.lua 文件,配置镜像代理规则,比如对所有 github.com 域名的访问切到 hub.fastgit.org 域名,实现加速下载包。 pac.lua 配置: ```lua function mirror(url) return url:gsub("github.com", "hub.fastgit.org") end ``` 然后我们设置这个 pac.lua 文件,默认路径在 `~/.xmake/pac.lua`,也可以手动配置使用指定位置的 pac.lua 。 ```console $ xmake g --proxy_pac=/tmp/pac.lua ``` 然后,我们安装包的时候,如果遇到 github.com 域名下的包源,下载时候会自动切到 fastgit 镜像加速下载。 ```console $ xrepo install libpng > curl https://hub.fastgit.org/glennrp/libpng/archive/v1.6.37.zip -o v1.6.37.zip ``` 因此,所有走预编译产物的下载也会得到提速,当然国内提供 github 镜像加速的不止 fastgit 一家,用户也可以切换到其他镜像源,比如 cnpmjs.org 等等。 #### 如何触发云端预编译 默认情况下,xmake 不会主动进行所有包的云端预编译缓存,这样太耗时耗力,目前仅仅只有提交 pr 到 [xmake-repo](https://github.com/xmake-io/xmake-repo) 官方包仓库,进行新包收录或者包版本更新时候,才会自动触发对应包的云端预编译行为。 所以,如果用户想要贡献包进我们的仓库,基本上都是可以被预编译缓存的(除了 headeronly 库),而如果用户不想贡献包,也想获取对应包的预编译加速,也是可以的。 只需要提交 pr 到 [build-artifacts](https://github.com/xmake-mirror/build-artifacts) 仓库的 build 分支,编辑 [build.txt](https://github.com/xmake-mirror/build-artifacts/blob/build/build.txt) 文件,修改里面需要触发预编译的包名和版本列表就行了,例如: build.txt ```lua { name = "asmjit", versions = { "2021.06.27" } } ``` 只要 pr 被 merge 之后,就会自动触发预编译行为,然后生成最终的编译产物到 releases 。 #### 强制源码编译安装 尽管我们提供了预编译下载安装的方式,但是如果用户还是想源码编译安装,我们也可以手动传入 `--build` 参数给 xrepo 命令,来强制切换到源码编译安装模式。 ```console $ xrepo install --build openssl ``` 在 xmake.lua 中,我们也可以同样支持源码编译安装。 ```lua add_requires("openssl", {build = true}) ``` 如果没有指定,那么 xmake 会自动优先尝试走预编译包的下载安装。 #### 添加私有预编译包仓库 我们的官方预编译包仓库在:[build-artifacts](https://github.com/xmake-mirror/build-artifacts)。 同样,我们也可以配置添加自有的预编译仓库,添加方式类似: ```console $ xmake repo --add local-repo git@github.com:xmake-mirror/myrepo-artifacts.git ``` 也可以在 xmake.lua 中添加: ```lua add_repositories("local-repo git@github.com:xmake-mirror/myrepo-artifacts.git") ``` ### 新版本地包方案 #### 默认打包格式 新版本中,我们提供了一种新的本地包打包方案,将会更加无缝的对接 `add_requires` 和 `add_packages`。 我们执行 `xmake package` 命令就能够生成默认的新版打包格式。 ```console $ xmake package package(foo): build/packages/f/foo generated ``` 它将会产生 `build/packages/f/foo/xmake.lua` 文件,内容如下: ```lua package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") on_load(function (package) package:set("installdir", path.join(os.scriptdir(), package:plat(), package:arch(), package:mode())) end) on_fetch(function (package) local result = {} result.links = "foo" result.linkdirs = package:installdir("lib") result.includedirs = package:installdir("include") return result end) ``` 其实就是采用 `package()` 来定义描述本地包,就跟远程包一样。 而生成的目录结构如下: ```console $ tree build/packages/f/foo/ build/packages/f/foo/ ├── macosx │   └── x86_64 │   └── release │   ├── include │   │   └── foo.h │   └── lib │   └── libfoo.a └── xmake.lua ``` 我们也能够使用 `add_requires`/`add_repositories` 接口来无缝集成这个包。 ```lua add_rules("mode.debug", "mode.release") add_repositories("local-repo build") add_requires("foo") target("bar") set_kind("binary") add_files("src/*.cpp") add_packages("foo") ``` 其中,`add_repositories` 配置指定本地包的仓库根目录,然后就可以通过 `add_requires` 来引用这个包了。 另外,生成的本地包,还有一个特性,就是支持 `target/add_deps`,会自动关联多个包的依赖关系,集成时候,也会自动对接所有依赖链接。 这里有完整的[测试例子](https://github.com/xmake-io/xmake/blob/dev/tests/actions/package/localpkg/test.lua)。 ```console "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/bar build/.objs/bar/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version-min=10.15 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk -stdlib=libc++ -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/f/foo/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/s/sub/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/a/add/macosx/x86_64/release/lib -Wl,-x -lfoo -lsub -ladd -lz ``` 备注:之前的老版本本地打包格式属于早期产物,还是会被保留,但是不推荐继续使用,想要继续使用,可以执行下面的命令打包: ```console $ xmake package -f oldpkg ``` #### 生成远程包 除了本地包格式,`xmake package` 现在也支持生成远程包,便于用户将他们快速提交到远程仓库。 我们只需要在打包时候,修改包格式。 ```console $ xmake package -f remote ``` 他也会产生 packages/f/foo/xmake.lua 文件。 ```lua package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") add_urls("https://github.com/myrepo/foo.git") add_versions("1.0", "") on_install(function (package) local configs = {} if package:config("shared") then configs.kind = "shared" end import("package.tools.xmake").install(package, configs) end) on_test(function (package) -- TODO check includes and interfaces -- assert(package:has_cfuncs("foo", {includes = "foo.h"}) end) ``` 包定义配置相比本地包,多了实际的安装逻辑,以及 urls 和 versions 的设置, 我们也能够通过附加参数,去修改 urls,versions 等配置值,例如: ```console $ xmake package -f remote --url=https://xxxx/xxx.tar.gz --shasum=xxxxx --homepage=xxxxx` ``` xmake 也会从 target 的 `set_license` 和 `set_version` 等配置中读取相关配置信息。 ### 从第三方仓库搜索包 xmake 内置的 xrepo 包管理器命令,之前可以支持搜索 xmake-repo 仓库中的内置包。 ```console $ xrepo search zlib "pcr*" zlib: -> zlib: A Massively Spiffy Yet Delicately Unobtrusive Compression Library (in xmake-repo) pcr*: -> pcre2: A Perl Compatible Regular Expressions Library (in xmake-repo) -> pcre: A Perl Compatible Regular Expressions Library (in xmake-repo) ``` 而现在,我们还可以从 vcpkg, conan, conda 以及 apt 等第三方包管理器中搜索它们的包,只需要加上对应的包命名空间就行,例如: ```console $ xrepo search vcpkg::pcre The package names: vcpkg::pcre: -> vcpkg::pcre-8.44#8: Perl Compatible Regular Expressions -> vcpkg::pcre2-10.35#2: PCRE2 is a re-working of the original Perl Compatible Regular Expressions library ``` ```console $ xrepo search conan::openssl The package names: conan::openssl: -> conan::openssl/1.1.1g: -> conan::openssl/1.1.1h: ``` ### 修改目标文件名 我们知道,对于目标文件名的修改,我们可以使用 `set_basename` 或者使用 `set_filename` 接口来配置实现,前者修改 `libxxx.so` 中的 `xxx` 部分名字,后者可以修改完整的文件名。 但是有些情况,我们仅仅想要修改:扩展名 `.so`,前缀名 `lib`,或者增加后缀名比如:`libxxx-d.so` 就会很麻烦,要么使用 `set_filename` 进行完整修改。 现在,我们新提供了 `set_prefixname`, `set_suffixname` 和 `set_extension` 三个独立接口来更加灵活地配置它们。 #### 设置目标文件的前置名 例如将默认的:`libtest.so` 改成 `test.so` ```lua target("test") set_prefixname("") ``` #### 设置目标文件的后置名 例如将默认的:`libtest.so` 改成 `libtest-d.so` ```lua target("test") set_suffixname("-d") ``` #### 设置目标文件的扩展名 例如将默认的:`libtest.so` 改成 `test.dll` ```lua target("test") set_prefixname("") set_extension(".dll") ``` ### 默认的目标类型 新版本中,如果用户没有对 target 设置 `set_kind` 指定目标类型,那么默认就是 binary 程序。 因此,我们可以实现更小的配置,例如: ```lua target("test") add_files("src/*.c") ``` 只需两行就可以完成一些小项目的编译,甚至可以更加简短: ```lua target("test", {files = "src/*.c"}) ``` ### 新增 appletvos 编译平台 我们还新增了一个 appletvos 的编译平台,用于支持 Apple 的 TVOS 系统上程序的编译,只需要: ```console $ xmake f -p appletvos $ xmake ``` ### 导入导出编译配置 我们还可以导入导出已经配置好的配置集,方便配置的快速迁移。 #### 导出配置 ```console $ xmake f --export=/tmp/config.txt $ xmake f -m debug --xxx=y --export=/tmp/config.txt ``` #### 导入配置 ```console $ xmake f --import=/tmp/config.txt $ xmake f -m debug --xxx=y --import=/tmp/config.txt ``` #### 导出配置(带菜单) ```console $ xmake f --menu --export=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --export=/tmp/config.txt ``` #### 导入配置(带菜单) ```console $ xmake f --menu --import=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --import=/tmp/config.txt ``` ### vs2022 支持 另外,新版本中,我们也增加了对 vs2020 预览版的支持。 ### 改进 xrepo shell 环境 在上个版本,我们支持了通过在当前目录下,添加 xmake.lua 文件,来定制化一些包配置,然后进入特定的包 shell 环境。 ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```console $ xrepo env shell > python --version > luajit --version ``` 而现在,我们还可以在 xmake.lua 配置加载对应的工具链环境,比如加载 vs 的编译环境。 ```lua set_toolchains("msvc") ``` ## 更新内容 ### 新特性 * [#1421](https://github.com/xmake-io/xmake/issues/1421): 针对 target 目标,增加目标文件名的前缀,后缀和扩展名设置接口。 * [#1422](https://github.com/xmake-io/xmake/issues/1422): 支持从 vcpkg, conan 中搜索包 * [#1424](https://github.com/xmake-io/xmake/issues/1424): 设置 binary 作为默认的 target 目标类型 * [#1140](https://github.com/xmake-io/xmake/issues/1140): 支持安装时候,手动选择从第三包包管理器安装包 * [#1339](https://github.com/xmake-io/xmake/issues/1339): 改进 `xmake package` 去产生新的本地包格式,无缝集成 `add_requires`,并且新增生成远程包支持 * 添加 `appletvos` 编译平台支持, `xmake f -p appletvos` * [#1437](https://github.com/xmake-io/xmake/issues/1437): 为包添加 headeronly 库类型去忽略 `vs_runtime` * [#1351](https://github.com/xmake-io/xmake/issues/1351): 支持导入导出当前配置 * [#1454](https://github.com/xmake-io/xmake/issues/1454): 支持下载安装 windows 预编译包 ### 改进 * [#1425](https://github.com/xmake-io/xmake/issues/1425): 改进 tools/meson 去加载 msvc 环境,并且增加一些内置配置。 * [#1442](https://github.com/xmake-io/xmake/issues/1442): 支持从 git url 去下载包资源文件 * [#1389](https://github.com/xmake-io/xmake/issues/1389): 支持添加工具链环境到 `xrepo env` * [#1453](https://github.com/xmake-io/xmake/issues/1453): 支持 protobuf 规则导出头文件搜索目录 * 新增对 vs2022 的支持 ### Bugs 修复 * [#1413](https://github.com/xmake-io/xmake/issues/1413): 修复查找包过程中出现的挂起卡死问题 * [#1420](https://github.com/xmake-io/xmake/issues/1420): 修复包检测和配置缓存 * [#1445](https://github.com/xmake-io/xmake/issues/1445): 修复 WDK 驱动签名错误 * [#1465](https://github.com/xmake-io/xmake/issues/1465): 修复缺失的链接目录 --- --- url: /posts/xmake-update-v2.5.6.md --- ### Fix windows precompiled package compatibility The previous version provided preliminary support for the installation of pre-compiled packages under Windows, but because the compatibility of the toolset version was not considered, if the user's VS version is too low, link problems will occur when the package is integrated. According to the official description of ms, the binary library of msvc is backward compatible with the version of toolset. [https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=msvc-160](https://xmake.io) > You can mix binaries built by different versions of the v140, v141, and v142 toolsets. However, you must link by using a toolset at least as recent as the most recent binary in your app. Here's an example: you can link an app compiled using any 2017 toolset (v141, versions 15.0 through 15.9) to a static library compiled using, say, Visual Studio 2019 version 16.2 (v142), if they're linked using a version 16.2 or later toolset. You can link a version 16.2 library to a version 16.4 app as long as you use a 16.4 or later toolset. In other words, the cloud uses the library compiled by v141, and the user's msvc toolset can be compatible and supported as long as it is >=141. Therefore, we have improved the pre-compilation logic of the cloud, and pre-compiled the two toolsets of vs2015/14.16 and vs2019/14.29 respectively, and then xmake will select the best compatible version library to download and integrate according to the user's msvc version of toolset. ### set\_defaultplat #### Set the default compilation platform Only supported by v2.5.6 and above, it is used to set the default compilation platform of the project. If it is not set, the default platform follows the current system platform, which is os.host(). For example, the default compilation platform on macOS is macosx, if the current project is an ios project, you can set the default compilation platform to iphoneos. ```lua set_defaultplat("iphoneos") ``` It is equivalent to `xmake f -p iphoneos`. ### set\_defaultarch #### Set the default compilation architecture Only supported by v2.5.6 and above, it is used to set the default compilation architecture of the project. If it is not set, the default platform follows the current system architecture, which is os.arch(). ```lua set_defaultplat("iphoneos") set_defaultarch("arm64") ``` It is equivalent to `xmake f -p iphoneos -a arm64`. We can also set the default architecture under multiple platforms. ```lua set_defaultarch("iphoneos|arm64", "windows|x64") ``` The arm64 architecture is compiled by default on iphoneos, and the x64 architecture is compiled by default on windows. ### set\_defaultmode #### Set the default compilation mode Only supported by v2.5.6 and above, it is used to set the default compilation mode of the project. If it is not set, the default is to compile in release mode. ```lua set_defaultmode("releasedbg") ``` It is equivalent to `xmake f -m releasedbg`. ### set\_allowedplats #### Set the list of platforms allowed to compile It is only supported by v2.5.6 and above. It is used to set the list of compilation platforms supported by the project. If the user specifies other platforms, an error will be prompted. This is usually used to restrict the user from specifying the wrong invalid platform. If it is not set, then there are no platform restrictions. ```lua set_allowedplats("windows", "mingw") ``` Set the current project to only support windows and mingw platforms. ### set\_allowedarchs #### Set the platform architecture that allows compilation Only supported by v2.5.6 and above. It is used to set the list of compiled architectures supported by the project. If the user specifies other architectures, an error will be prompted. This is usually used to restrict users from specifying incorrect invalid architectures. If it is not set, then there are no architectural restrictions. ```lua set_allowedarchs("x64", "x86") ``` The current project only supports x64/x86 platforms. We can also specify the list of architectures allowed under multiple platforms at the same time. ```lua set_allowedarchs("windows|x64", "iphoneos|arm64") ``` Set the current project to only support x64 architecture on windows, and only support arm64 architecture on iphoneos. ### set\_allowedmodes #### Set the list of allowed compilation modes It is only supported by v2.5.6 and above. It is used to set the list of compilation modes supported by the project. If the user specifies other modes, an error will be prompted. This is usually used to restrict the user from specifying incorrect invalid modes. If it is not set, then there is no mode restriction. ```lua set_allowedmodes("release", "releasedbg") ``` Set the current project to only support the two compilation modes release/releasedbg. ## Changelog ### New features * [#1483](https://github.com/xmake-io/xmake/issues/1483): Add `os.joinenvs()` and improve package tools envirnoments * [#1523](https://github.com/xmake-io/xmake/issues/1523): Add `set_allowedmodes`, `set_allowedplats` and `set_allowedarchs` * [#1523](https://github.com/xmake-io/xmake/issues/1523): Add `set_defaultmode`, `set_defaultplat` and `set_defaultarch` ### Change * Improve vs/vsxmake project generator to support vs2022 * [#1513](https://github.com/xmake-io/xmake/issues/1513): Improve precompiled binary package compatibility on windows/msvc * Improve to find vcpkg root directory on windows * Improve to support Qt6 ### Bugs fixed * [#489](https://github.com/xmake-io/xmake-repo/pull/489): Fix run os.execv with too long envirnoment value on windows --- --- url: /zh/posts/xmake-update-v2.5.6.md --- 这是一个稳定性修复版本,主要修复和改进了一些跟预编译二进制包相关的兼容性问题。另外新增了一些实用的接口来设置默认的编译平台、架构和模式,以及允许的编译平台、架构列表等等。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) * [入门课程](https://xmake.io/zh/) ## 新特性介绍 ### windows 预编译包的兼容性修复 上个版本对 Windows 下的 预编译包安装做了初步的支持,但是由于没有考虑 toolset 版本的兼容性问题,因此如果用户的 vs 版本过低,就会在集成包时候出现链接问题。 根据 ms 的官方描述,其实 msvc 的二进制库对于 toolset 的版本是向下兼容的。 > You can mix binaries built by different versions of the v140, v141, and v142 toolsets. However, you must link by using a toolset at least as recent as the most recent binary in your app. Here's an example: you can link an app compiled using any 2017 toolset (v141, versions 15.0 through 15.9) to a static library compiled using, say, Visual Studio 2019 version 16.2 (v142), if they're linked using a version 16.2 or later toolset. You can link a version 16.2 library to a version 16.4 app as long as you use a 16.4 or later toolset. 也就是说,云端采用 v141 编译的库,用户的 msvc toolset 只要是 >=141 就可以兼容支持。 因此,我们改进了云端的预编译逻辑,针对 vs2015/14.16 和 vs2019/14.29 两个工具集分别进行预编译,然后 xmake 会根据用户 msvc 的 toolset 版本,优先选取最优的兼容版本库下载集成。 ### set\_defaultplat #### 设置默认的编译平台 v2.5.6 以上版本才支持,用于设置工程默认的编译平台,如果没有设置,默认平台跟随当前系统平台,也就是 os.host()。 比如,在 macOS 上默认编译平台是 macosx,如果当前项目是 ios 项目,那么可以设置默认编译平台为 iphoneos。 ```lua set_defaultplat("iphoneos") ``` 它等价于,`xmake f -p iphoneos`。 ### set\_defaultarch #### 设置默认的编译架构 v2.5.6 以上版本才支持,用于设置工程默认的编译架构,如果没有设置,默认平台跟随当前系统架构,也就是 os.arch()。 ```lua set_defaultplat("iphoneos") set_defaultarch("arm64") ``` 它等价于,`xmake f -p iphoneos -a arm64`。 我们也可以设置多个平台下的默认架构。 ```lua set_defaultarch("iphoneos|arm64", "windows|x64") ``` 在 iphoneos 上默认编译 arm64 架构,在 windows 上默认编译 x64 架构。 ### set\_defaultmode #### 设置默认的编译模式 v2.5.6 以上版本才支持,用于设置工程默认的编译模式,如果没有设置,默认是 release 模式编译。 ```lua set_defaultmode("releasedbg") ``` 它等价于,`xmake f -m releasedbg`。 ### set\_allowedplats #### 设置允许编译的平台列表 v2.5.6 以上版本才支持,用于设置工程支持的编译平台列表,如果用户指定了其他平台,会提示错误,这通常用于限制用户指定错误的无效平台。 如果没有设置,那么没有任何平台限制。 ```lua set_allowedplats("windows", "mingw") ``` 设置当前项目仅仅支持 windows 和 mingw 平台。 ### set\_allowedarchs #### 设置允许编译的平台架构 v2.5.6 以上版本才支持,用于设置工程支持的编译架构列表,如果用户指定了其他架构,会提示错误,这通常用于限制用户指定错误的无效架构。 如果没有设置,那么没有任何架构限制。 ```lua set_allowedarchs("x64", "x86") ``` 当前项目,仅仅支持 x64/x86 平台。 我们也可以同时指定多个平台下允许的架构列表。 ```lua set_allowedarchs("windows|x64", "iphoneos|arm64") ``` 设置当前项目在 windows 上仅仅支持 x64 架构,并且在 iphoneos 上仅仅支持 arm64 架构。 ### set\_allowedmodes #### 设置允许的编译模式列表 v2.5.6 以上版本才支持,用于设置工程支持的编译模式列表,如果用户指定了其他模式,会提示错误,这通常用于限制用户指定错误的无效模式。 如果没有设置,那么没有任何模式限制。 ```lua set_allowedmodes("release", "releasedbg") ``` 设置当前项目仅仅支持 release/releasedbg 两个编译模式。 ## 更新内容 ### 新特性 * [#1483](https://github.com/xmake-io/xmake/issues/1483): 添加 `os.joinenvs()` 和改进包工具环境 * [#1523](https://github.com/xmake-io/xmake/issues/1523): 添加 `set_allowedmodes`, `set_allowedplats` 和 `set_allowedarchs` * [#1523](https://github.com/xmake-io/xmake/issues/1523): 添加 `set_defaultmode`, `set_defaultplat` 和 `set_defaultarch` ### 改进 * 改进 vs/vsxmake 工程插件支持 vs2022 * [#1513](https://github.com/xmake-io/xmake/issues/1513): 改进 windows 预编译包的兼容性问题 * 改进 vcpkg 包在 windows 上的查找 * 改进对 Qt6 的支持 ### Bugs 修复 * [#489](https://github.com/xmake-io/xmake-repo/pull/489): 修复 run os.execv 带有过长环境变量值出现的一些问题 --- --- url: /posts/xmake-update-v2.5.7.md --- ### Added Vala language support In this version, we can already initially support the construction of Vala programs, just apply the `add_rules("vala")` rule. At the same time, we need to add some dependency packages, among which the glib package is necessary because Vala itself will also use it. `add_values("vala.packages")` is used to tell valac which packages the project needs, it will introduce the vala api of the relevant package, but the dependency integration of the package still needs to be downloaded and integrated through `add_requires("lua")`. E.g: ```lua add_rules("mode.release", "mode.debug") add_requires("lua", "glib") target("test") set_kind("binary") add_rules("vala") add_files("src/*.vala") add_packages("lua", "glib") add_values("vala.packages", "lua") ``` More examples: [Vala examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/vala) ### Added package dependency lock support This feature is similar to npm's package.lock and cargo's cargo.lock. For example, if we quote some packages, by default, if the version is not specified, xmake will automatically pull the latest version of the package for integrated use each time, for example: ```lua add_requires("zlib") ``` However, if the upstream package repository is updated and changed, for example, zlib adds a new version 1.2.11, or the installation script is changed, the user's dependent packages will change. This can easily lead to some projects that were originally compiled and passed, and there may be some unstable factors due to changes in dependent packages, and the compilation may fail, etc. In order to ensure that the package used by the user's project is fixed each time, we can enable the package dependency lock through the following configuration. ```lua set_policy("package.requires_lock", true) ``` This is a global setting and must be set to the global root scope. If enabled, xmake will automatically generate a `xmake-requires.lock` configuration file after executing package pull. It contains all the packages that the project depends on, as well as the current package version and other information. ```lua { __meta__ = { version = "1.0" }, ["macosx|x86_64"] = { ["cmake#31fecfc4"] = { repo = { branch = "master", commit = "4498f11267de5112199152ab030ed139c985ad5a", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.21.0" }, ["glfw#31fecfc4"] = { repo = { branch = "master", commit = "eda7adee81bac151f87c507030cc0dd8ab299462", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.3.4" }, ["opengl#31fecfc4"] = { repo = { branch = "master", commit = "94d2eee1f466092e04c5cf1e4ecc8c8883c1d0eb", url = "https://github.com/xmake-io/xmake-repo.git" } } } } ``` Of course, we can also execute the following command to force the upgrade package to the latest version. ```console $ xmake require --upgrade upgrading packages .. zlib: 1.2.10 -> 1.2.11 1 package is upgraded! ``` ### option supports runtime detection of code snippets Option itself provides two interfaces `add_csnippets/add_cxxsnippets`, which are used to quickly detect whether a specific piece of c/c++ code has been compiled, and if the compilation passes, the corresponding option option will be enabled. But the previous version can only provide compile-time detection, and in the new version, we also added runtime detection support. We can set the two parameters `{tryrun = true}` and `{output = true}` to try to run detection and capture output. #### Try to run the test Set tryrun to try to run to detect ```lua option("test") add_cxxsnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0: -1;", {tryrun = true}) ``` If the compile and run pass, the test option will be enabled. #### Detect and capture output at runtime Setting output will also try to detect and additionally capture the output content of the run. ```lua option("test") add_cxxsnippets("INT_SIZE",'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` If the compile and run pass, the test option will be enabled, and the corresponding output content can be obtained as the value of option. Note: Set to capture output, the current option cannot set other snippets We can also get the output bound to the option through `is_config`. ```lua if is_config("test", "8") tben - xxx end ``` In addition, we have also improved the auxiliary detection interface of `includes("check_csnippets")` to support runtime detection. ```lua includes("check_csnippets.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") check_csnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0: -1;", {tryrun = true}) check_csnippets("INT_SIZE",'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) configvar_check_csnippets("HAS_LONG_8", "return (sizeof(long) == 8)? 0: -1;", {tryrun = true}) configvar_check_csnippets("PTR_SIZE",'printf("%d", sizeof(void*)); return 0;', {output = true, number = true}) ``` If capture output is enabled, `${define PTR_SIZE}` in `config.h.in` will automatically generate `#define PTR_SIZE 4`. Among them, the `number = true` setting can be forced as a number instead of a string value, otherwise it will be defined as `#define PTR_SIZE "4"` by default ### Quickly embed binary resource files into code We can use the `utils.bin2c` rule to introduce some binary files into the project, and see them as c/c++ header files for developers to use to obtain the data of these files. For example, we can embed some png/jpg resource files into the code in the project. ```lua target("console") set_kind("binart") add_rules("utils.bin2c", {extensions = {".png", ".jpg"}}) add_files("src/*.c") add_files("res/*.png", "res/*.jpg") ``` Note: The setting of extensions is optional, the default suffix is ​​.bin Then, we can import and use it through `#include "filename.png.h"`, xmake will automatically generate the corresponding header file for you, and add the corresponding search directory. ```c static unsigned char g_png_data[] = { #include "image.png.h" }; int main(int argc, char** argv) { printf("image.png: %s, size: %d\n", g_png_data, sizeof(g_png_data)); return 0; } ``` The content of the generated header file is similar: ```console cat build/.gens/test/macosx/x86_64/release/rules/c++/bin2c/image.png.h 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x78, 0x6D, 0x61, 0x6B, 0x65, 0x21, 0x0A, 0x00 ``` ### Added iOS/macOS application Metal compilation support We know that the `xcode.application` rule can compile iOS/macOS applications, generate .app/.ipa packages, and complete the signature operation at the same time. However, it did not support the compilation of code with .metal before. In the new version, we have added the `xcode.metal` rule, which is associated with the `xcode.application` rule by default to support metal compilation by default. xmake will automatically compile .metal and then package to generate the default.metallib file, and it will be built into .app/.ipa automatically. If the user's metal is accessed through `[_device newDefaultLibrary]`, it can be automatically supported, just like compiling with xcode. Here is a complete one we provide: [Project Example](https://github.com/xmake-io/xmake/blob/master/tests/projects/objc/metal_app/xmake.lua). ```lua add_rules("mode.debug", "mode.release") target("HelloTriangle") add_rules("xcode.application") add_includedirs("Renderer") add_frameworks("MetalKit") add_mflags("-fmodules") add_files("Renderer/*.m", "Renderer/*.metal") ------- add metal files if is_plat("macosx") then add_files("Application/main.m") add_files("Application/AAPLViewController.m") add_files("Application/macOS/Info.plist") add_files("Application/macOS/Base.lproj/*.storyboard") add_defines("TARGET_MACOS") add_frameworks("AppKit") elseif is_plat("iphoneos") then add_files("Application/*.m") add_files("Application/iOS/Info.plist") add_files("Application/iOS/Base.lproj/*.storyboard") add_frameworks("UIKit") add_defines("TARGET_IOS") ``` For example, on macOS, after compiling and running, the desired effect will be rendered through metal. ![](/assets/img/posts/xmake/xmake-metal.png) If our project does not use the default metal library, we can also use the above-mentioned `utils.bin2c` rule as a source file and embed it into the code library, for example: ```lua add_rules("utils.bin2c", {extensions = ".metal"}) add_files("Renderer/*.metal") ``` Then in the code, we can access: ```c static unsigned char g_metal_data[] = { #include "xxx.metal.h" }; id library = [_device newLibraryWithSource:[[NSString stringWithUTF8String:g_metal_data]] options:nil error:&error]; ``` ### Improve add\_repositories If we use the local repository built into the project, we used to introduce it through `add_repositories("myrepo repodir")`. However, it is not based on the relative directory of the current xmake.lua file directory like `add_files()`, and there is no automatic path conversion, so it is easy to encounter the problem of not being able to find the repo. Therefore, we have improved it, and you can specify the corresponding root directory location through an additional rootdir parameter, such as the script directory relative to the current xmake.lua. ```lua add_repositories("myrepo repodir", {rootdir = os.scriptdir()}) ``` ### os.cp supports symbolic links In the previous version, the `os.cp` interface could not handle the copying of symbolic links very well. It would automatically expand the link and copy the actual file content, which would only cause the symbolic link to be lost after copying. If you want to keep the symbolic link as it is after copying, you only need to set the following parameter: `{symlink = true}` ```lua os.cp("/xxx/symlink", "/xxx/dstlink", {symlink = true}) ``` ### Compile automatically generated code more easily Sometimes, we have such a requirement to automatically generate some source files to participate in the later code compilation before compilation. But because the files added by `add_files` are already determined when the compilation is executed, they cannot be added dynamically during the compilation process (because parallel compilation is required). Therefore, to achieve this requirement, we usually need to customize a rule, and then actively call the compiler module to deal with a series of issues such as compilation of generated code, injection of object files, and dependency updates. This is not a big problem for the xmake developers themselves, but for users, it is still more cumbersome and difficult to get started. In the new version, we have improved the support for `add_files` and added the `{always_added = true}` configuration to tell xmake that we always need to add the specified source file, even if it does not exist yet. In this way, we can rely on the default compilation process of xmake to compile the automatically generated code, like this: ```lua add_rules("mode.debug", "mode.release") target("autogen_code") set_kind("binary") add_files("$(buildir)/autogen.cpp", {always_added = true}) before_build(function (target) io.writefile("$(buildir)/autogen.cpp", [[ #include using namespace std; int main(int argc, char** argv) { cout << "hello world!" << endl; return 0; } ]]) end) ``` There is no need for additional rule definitions, only the compilation order needs to be guaranteed, and the code files are generated at the correct stage. However, we also need to pay attention that since the currently automatically generated source files may not yet exist, we cannot use pattern matching in `add_files`, and can only explicitly add each source file path. ## Changelog ### New features * [#1534](https://github.com/xmake-io/xmake/issues/1534): Support to compile Vala lanuage project * [#1544](https://github.com/xmake-io/xmake/issues/1544): Add utils.bin2c rule to generate header from binary file * [#1547](https://github.com/xmake-io/xmake/issues/1547): Support to run and get output of c/c++ snippets in option * [#1567](https://github.com/xmake-io/xmake/issues/1567): Package "lock file" support to freeze dependencies * [#1597](https://github.com/xmake-io/xmake/issues/1597): Support to compile \*.metal files to generate \*.metalib and improve xcode.application rule ### Change * [#1540](https://github.com/xmake-io/xmake/issues/1540): Better support for compilation of automatically generated code * [#1578](https://github.com/xmake-io/xmake/issues/1578): Improve add\_repositories to support relative path better * [#1582](https://github.com/xmake-io/xmake/issues/1582): Improve installation and os.cp to reserve symlink ### Bugs fixed * [#1531](https://github.com/xmake-io/xmake/issues/1531): Fix error info when loading targets failed --- --- url: /zh/posts/xmake-update-v2.5.7.md --- ## 新特性介绍 ### 新增 Vala 语言支持 这个版本,我们已经可以初步支持构建 Vala 程序,只需要应用 `add_rules("vala")` 规则。 同时,我们需要添加一些依赖包,其中 glib 包是必须的,因为 vala 自身也会用到它。 `add_values("vala.packages")` 用于告诉 valac,项目需要哪些包,它会引入相关包的 vala api,但是包的依赖集成,还是需要通过 `add_requires("lua")` 下载集成。 例如: ```lua add_rules("mode.release", "mode.debug") add_requires("lua", "glib") target("test") set_kind("binary") add_rules("vala") add_files("src/*.vala") add_packages("lua", "glib") add_values("vala.packages", "lua") ``` 更多例子:[Vala examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/vala) ### 新增包依赖锁定支持 这个特性类似 npm 的 package.lock, cargo 的 cargo.lock。 比如,我们引用一些包,默认情况下,如果不指定版本,那么 xmake 每次都会自动拉取最新版本的包来集成使用,例如: ```lua add_requires("zlib") ``` 但如果上游的包仓库更新改动,比如 zlib 新增了一个 1.2.11 版本,或者安装脚本有了变动,都会导致用户的依赖包发生改变。 这容易导致原本编译通过的一些项目,由于依赖包的变动出现一些不稳定因素,有可能编译失败等等。 为了确保用户的项目每次使用的包都是固定的,我们可以通过下面的配置去启用包依赖锁定。 ```lua set_policy("package.requires_lock", true) ``` 这是一个全局设置,必须设置到全局根作用域,如果启用后,xmake 执行完包拉取,就会自动生成一个 `xmake-requires.lock` 的配置文件。 它包含了项目依赖的所有包,以及当前包的版本等信息。 ```lua { __meta__ = { version = "1.0" }, ["macosx|x86_64"] = { ["cmake#31fecfc4"] = { repo = { branch = "master", commit = "4498f11267de5112199152ab030ed139c985ad5a", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.21.0" }, ["glfw#31fecfc4"] = { repo = { branch = "master", commit = "eda7adee81bac151f87c507030cc0dd8ab299462", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.3.4" }, ["opengl#31fecfc4"] = { repo = { branch = "master", commit = "94d2eee1f466092e04c5cf1e4ecc8c8883c1d0eb", url = "https://github.com/xmake-io/xmake-repo.git" } } } } ``` 当然,我们也可以执行下面的命令,强制升级包到最新版本。 ```console $ xmake require --upgrade upgrading packages .. zlib: 1.2.10 -> 1.2.11 1 package is upgraded! ``` ### option 支持代码片段的运行时检测 option 本身有提供 `add_csnippets/add_cxxsnippets` 两个接口,用于快速检测特定一段 c/c++ 代码是否通过编译,如果编译通过就会启用对应 option 选项。 但之前的版本仅仅只能提供编译期检测,而新版本中,我们还新增了运行时检测支持。 我们可以通过设置 `{tryrun = true}` 和 `{output = true}` 两个参数,用于尝试运行检测和捕获输出。 #### 尝试运行检测 设置 tryrun 可以尝试运行来检测 ```lua option("test") add_cxxsnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0 : -1;", {tryrun = true}) ``` 如果编译运行通过,test 选项就会被启用。 #### 运行时检测并捕获输出 设置 output 也会尝试去检测,并且额外捕获运行的输出内容。 ```lua option("test") add_cxxsnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` 如果编译运行通过,test 选项就会被启用,同时能获取到对应的输出内容作为 option 的值。 注:设置为捕获输出,当前 option 不能再设置其他 snippets 我们也可以通过 `is_config` 获取绑定到option的输出。 ```lua if is_config("test", "8") tben -- xxx end ``` 另外,我们也对 `includes("check_csnippets")` 的辅助检测接口,也做了改进来支持运行时检测。 ```lua includes("check_csnippets.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") check_csnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0 : -1;", {tryrun = true}) check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) configvar_check_csnippets("HAS_LONG_8", "return (sizeof(long) == 8)? 0 : -1;", {tryrun = true}) configvar_check_csnippets("PTR_SIZE", 'printf("%d", sizeof(void*)); return 0;', {output = true, number = true}) ``` 如果启用捕获输出,`config.h.in` 的 `${define PTR_SIZE}` 会自动生成 `#define PTR_SIZE 4`。 其中,`number = true` 设置,可以强制作为 number 而不是字符串值,否则默认会定义为 `#define PTR_SIZE "4"` ### 快速内嵌二进制资源文件到代码 我们可以通过 `utils.bin2c` 规则,在项目中引入一些二进制文件,并见他们作为 c/c++ 头文件的方式提供开发者使用,获取这些文件的数据。 比如,我们可以在项目中,内嵌一些 png/jpg 资源文件到代码中。 ```lua target("console") set_kind("binart") add_rules("utils.bin2c", {extensions = {".png", ".jpg"}}) add_files("src/*.c") add_files("res/*.png", "res/*.jpg") ``` 注:extensions 的设置是可选的,默认后缀名是 .bin 然后,我们就可以通过 `#include "filename.png.h"` 的方式引入进来使用,xmake 会自动帮你生成对应的头文件,并且添加对应的搜索目录。 ```c static unsigned char g_png_data[] = { #include "image.png.h" }; int main(int argc, char** argv) { printf("image.png: %s, size: %d\n", g_png_data, sizeof(g_png_data)); return 0; } ``` 生成头文件内容类似: ```console cat build/.gens/test/macosx/x86_64/release/rules/c++/bin2c/image.png.h 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x78, 0x6D, 0x61, 0x6B, 0x65, 0x21, 0x0A, 0x00 ``` ### 新增 iOS/macOS 应用 Metal 编译支持 我们知道 `xcode.application` 规则可以编译 iOS/macOS 应用程序,生成 .app/.ipa 程序包,并同时完成签名操作。 不过之前它不支持对带有 .metal 代码的编译,而新版本中,我们新增了 `xcode.metal` 规则,并默认关联到 `xcode.application` 规则中去来默认支持 metal 编译。 xmake 会自动编译 .metal 然后打包生成 default.metallib 文件,并且自动内置到 .app/.ipa 里面。 如果用户的 metal 是通过 `[_device newDefaultLibrary]` 来访问的,那么就能自动支持,就跟使用 xcode 编译一样。 这里是我们提供的一个完整的:[项目例子](https://github.com/xmake-io/xmake/blob/master/tests/projects/objc/metal_app/xmake.lua)。 ```lua add_rules("mode.debug", "mode.release") target("HelloTriangle") add_rules("xcode.application") add_includedirs("Renderer") add_frameworks("MetalKit") add_mflags("-fmodules") add_files("Renderer/*.m", "Renderer/*.metal") ------- 添加 metal 文件 if is_plat("macosx") then add_files("Application/main.m") add_files("Application/AAPLViewController.m") add_files("Application/macOS/Info.plist") add_files("Application/macOS/Base.lproj/*.storyboard") add_defines("TARGET_MACOS") add_frameworks("AppKit") elseif is_plat("iphoneos") then add_files("Application/*.m") add_files("Application/iOS/Info.plist") add_files("Application/iOS/Base.lproj/*.storyboard") add_frameworks("UIKit") add_defines("TARGET_IOS") ``` 比如,在 macOS 上,编译运行后,就会通过 metal 渲染出需要的效果。 ![](/assets/img/posts/xmake/xmake-metal.png) 如果,我们的项目没有使用默认的 metal library,我们也可以通过上面提到的 `utils.bin2c` 规则,作为源文件的方式内嵌到代码库中,例如: ```lua add_rules("utils.bin2c", {extensions = ".metal"}) add_files("Renderer/*.metal") ``` 然后代码中,我们就能访问了: ```c static unsigned char g_metal_data[] = { #include "xxx.metal.h" }; id library = [_device newLibraryWithSource:[[NSString stringWithUTF8String:g_metal_data]] options:nil error:&error]; ``` ### 改进 add\_repositories 如果我们通过内置在项目中的本地仓库,我们之前是通过 `add_repositories("myrepo repodir")` 的方式来引入。 但是,它并不像 `add_files()` 那样是基于当前 xmake.lua 文件目录的相对目录,也没有路径的自动转换,因此容易遇到找不到 repo 的问题。 因此,我么你改进了下它,可以通过额外的 rootdir 参数指定对应的根目录位置,比如相对当前 xmake.lua 的脚本目录。 ```lua add_repositories("myrepo repodir", {rootdir = os.scriptdir()}) ``` ### os.cp 支持符号链接 之前的版本,`os.cp` 接口不能很好的处理符号链接的复制,他会自动展开链接,复制实际的文件内容,只会导致复制后,符号链接丢失。 如果想要复制后,原样保留符号链接,只需要设置下参数:`{symlink = true}` ```lua os.cp("/xxx/symlink", "/xxx/dstlink", {symlink = true}) ``` ### 更方便地编译自动生成的代码 有时候,我们会有这样一个需求,在编译前,自动生成一些源文件参与后期的代码编译。但是由于 `add_files` 添加的文件在执行编译时候,就已经确定,无法在编译过程中动态添加它们(因为需要并行编译)。 因此,要实现这个需求,我们通常需要自定义一个 rule,然后里面主动调用编译器模块去处理生成代码的编译,对象文件的注入,依赖更新等一系列问题。 这对于 xmake 开发者本身没什么大问题,但是对于用户来说,这还是比较繁琐了,不好上手。 新版本中,我们改进了对 `add_files` 的支持,并添加了 `{always_added = true}` 配置来告诉 xmake 我们始终需要添加指定的源文件,即使它还不存在。 这样我们就可以依靠xmake的默认编译过程来编译自动生成的代码了,像这样: ```lua add_rules("mode.debug", "mode.release") target("autogen_code") set_kind("binary") add_files("$(buildir)/autogen.cpp", {always_added = true}) before_build(function (target) io.writefile("$(buildir)/autogen.cpp", [[ #include using namespace std; int main(int argc, char** argv) { cout << "hello world!" << endl; return 0; } ]]) end) ``` 都不需要额外的 rule 定义,只需要保证编译顺序,在正确的阶段生成代码文件就可以了。 但是,我们也需要注意,由于当前自动生成的源文件可能还不存在,我们不能在 `add_files` 里面使用模式匹配,只能显式添加每个源文件路径。 ## 更新内容 ### 新特性 * [#1534](https://github.com/xmake-io/xmake/issues/1534): 新增对 Vala 语言的支持 * [#1544](https://github.com/xmake-io/xmake/issues/1544): 添加 utils.bin2c 规则去自动从二进制资源文件产生 .h 头文件并引入到 C/C++ 代码中 * [#1547](https://github.com/xmake-io/xmake/issues/1547): option/snippets 支持运行检测模式,并且可以获取输出 * [#1567](https://github.com/xmake-io/xmake/issues/1567): 新增 xmake-requires.lock 包依赖锁定支持 * [#1597](https://github.com/xmake-io/xmake/issues/1597): 支持编译 metal 文件到 metallib,并改进 xcode.application 规则去生成内置的 default.metallib 到 app ### 改进 * [#1540](https://github.com/xmake-io/xmake/issues/1540): 更好更方便地编译自动生成的代码 * [#1578](https://github.com/xmake-io/xmake/issues/1578): 改进 add\_repositories 去更好地支持相对路径 * [#1582](https://github.com/xmake-io/xmake/issues/1582): 改进安装和 os.cp 支持符号链接 ### Bugs 修复 * [#1531](https://github.com/xmake-io/xmake/issues/1531): 修复 targets 加载失败的错误信息提示错误 --- --- url: /posts/xmake-update-v2.5.8.md --- ### Pascal language support Currently, we can use the cross-platform Free Pascal toolchain fpc to compile and build Pascal programs, for example: #### Console Program ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.pas") ``` #### Dynamic library program ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.pas") target("test") set_kind("binary") add_deps("foo") add_files("src/main.pas") ``` We can also add compilation options related to Pascal code through the `add_fcflags()` interface. For more examples, see: [Pascal examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/pascal) ### Vala library compilation support In the last version, we added support for the Vala language, but before, we could only support the compilation of console programs, and could not generate library files. In this version, we have added additional compilation support for static libraries and dynamic libraries. #### Static library program We can set the exported interface header file name by `add_values("vala.header", "mymath.h")`, and set the exported vapi by `add_values("vala.vapi", "mymath-1.0.vapi")` file name. ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("static") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` #### Dynamic library program ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("shared") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` For more examples, see: [Vala examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/vala) ### Swig module support We provide `swig.c` and `swig.cpp` rules, which can call the swig program to generate the c/c++ module interface code for the specified script language, and then cooperate with the xmake package management system to achieve fully automated module and dependency package integration . Related issues: [#1622](https://github.com/xmake-io/xmake/issues/1622) #### Lua/C Module ```lua add_rules("mode.release", "mode.debug") add_requires("lua") target("example") add_rules("swig.c", {moduletype = "lua"}) add_files("src/example.i", {swigflags = "-no-old-metatable-bindings"}) add_files("src/example.c") add_packages("lua") ``` Among them, swigflags can be set to pass some swig-specific flags options. #### Python/C module ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.c", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.c") add_packages("python") ``` If scriptdir is set, then when we perform the installation, the python wrap script of the corresponding module will be installed to the specified directory. #### Python/C++ Module ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.cpp", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.cpp") add_packages("python") ``` ### Lua5.3 runtime support Xmake has always used Luajit as the default runtime, because it was considered that Luajit is relatively faster, and the fixed Lua 5.1 syntax is more suitable for the needs of xmake's internal implementation. However, considering that Luajit's update is not strong, the author's maintenance is not very active, and its cross-platform performance is relatively poor. For some new architectures, such as Loongarch, riscv, etc., the support is not timely, which somewhat limits the platform support of xmake. . For this reason, in the new version, we also built Lua5.3 as an optional runtime. We only need to compile and install xmake with the following command to switch from Luajit to Lua5.3 runtime: #### Linux/macOS ```bash $ make RUNTIME=lua ``` #### Windows ```bash $ cd core $ xmake f --runtime=lua $ xmake ``` At present, the current version is still the default luajit runtime. Users can switch to Lua5.3 runtime according to their needs, but this has almost no compatibility impact on the user's project xmake.lua configuration script. Because the configuration interface of xmake has already done a layer of abstract encapsulation, some native interfaces with compatibility differences in Luajit/Lua5.3 will not be open to users, so it is completely unaware for project construction. The only difference is that xmake with Lua5.3 supports more platforms and architectures. #### Performance comparison I have done some basic build tests. Whether it is startup time, build performance or memory usage, Lua5.3 and Luajit's xmake are almost the same. Because for the build system, the main performance bottleneck is the compiler, and the loss of its own scripts is very small. Moreover, some low-level Lua modules inside xmake, such as io, character encoding, string manipulation, etc., have all been rewritten in c code by themselves, and do not rely on a specific Lua runtime engine at all. #### Will you consider switching to Lua by default? Since we have just supported Lua5.3, although it is relatively stable after testing, in order to ensure that the user environment is not affected in any way, we still need to observe for a period of time. In the short term, we still use Luajit by default. When the 2.6.1 version starts, we will start to switch to Lua5.3 as the default runtime environment. If you are interested, you can also help test it online. If you encounter any problems, please feel free to report on issues. #### LoongArch architecture support Since we added Lua5.3 runtime support, we can now support running xmake on the LoongArch architecture, and all test examples have been tested. #### Lua 5.4 At present, we are still on the sidelines of Lua 5.4. If we wait for Lua 5.4 to become stable later, we will also try to consider continuing to upgrade to Lua 5.4. ### Third-party source code mixed compilation support #### Integrated CMake source library In the new version, we have been able to directly integrate the source library with CMakeLists.txt in our project through the package mode of xmake, instead of downloading and installing it remotely. Related issues: [#1714](https://github.com/xmake-io/xmake/issues/1714) For example, we have the following project structure: ``` ├── foo │ ├── CMakeLists.txt │ └── src │ ├── foo.c │ └── foo.h ├── src │ └── main.c ├── test.lua └── xmake.lua ``` The foo directory is a static library maintained by cmake, and the root directory is maintained by xmake. We can define the `package("foo")` package in xmake.lua to describe how to build the foo code library. ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` Among them, we set the code directory location of the foo package through `set_sourcedir()`, and then import the auxiliary module of `package.tools.cmake` through import to call cmake to build the code, xmake will automatically obtain the generated libfoo.a and the corresponding header document. :::NOTE If only the local source code is integrated, we don't need to set additional `add_urls` and `add_versions`. ::: For the configuration description of the package, see: [Package description description](/guide/package-management/package-distribution). After defining the package, we can integrate it with `add_requires("foo")` and `add_packages("foo")`, just like integrating remote packages. In addition, `on_test` is optional. If you want to strictly check whether the package is compiled and installed successfully, you can do some tests in it. For a complete example, see: [Library with CMakeLists](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/library_with_cmakelists) #### Integrate autoconf source library We can also use `package.tools.autoconf` to locally integrate third-party code libraries maintained by autoconf. ```lua package("pcre2") set_sourcedir(path.join(os.scriptdir(), "3rd/pcre2")) add_configs("jit", {description = "Enable jit.", default = true, type = "boolean"}) add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}}) on_load(function (package) local bitwidth = package:config("bitwidth") or "8" package:add("links", "pcre2-" .. bitwidth) package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth) if not package:config("shared") then package:add("defines", "PCRE2_STATIC") end end) on_install("macosx", "linux", "mingw", function (package) local configs = {} table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) if package:debug() then table.insert(configs, "--enable-debug") end if package:config("pic") ~= false then table.insert(configs, "--with-pic") end if package:config("jit") then table.insert(configs, "--enable-jit") end local bitwidth = package:config("bitwidth") or "8" if bitwidth ~= "8" then table.insert(configs, "--disable-pcre2-8") table.insert(configs, "--enable-pcre2-" .. bitwidth) end import("package.tools.autoconf").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("pcre2_compile", {includes = "pcre2.h"})) end) ``` Both `package.tools.autoconf` and `package.tools.cmake` modules can support cross-compilation platforms and toolchains such as mingw/cross/iphoneos/android, xmake will automatically pass the corresponding toolchain into it, and the user does not need to do Anything else. #### Integrate with other build systems We also support the integration of code libraries maintained by other build systems such as Meson/Scons/Make. You only need to import the corresponding build auxiliary modules. I won’t go into details here. We can further check the documentation: [Integrate local third-party source code libraries ](/guide/package-management/using-official-packages) ### Improve compiler feature detection In the previous version, we can use the `check_features` auxiliary interface to detect specific compiler features, such as: ```lua includes("check_features.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_features("HAS_CONSTEXPR", "cxx_constexpr") configvar_check_features("HAS_CONSEXPR_AND_STATIC_ASSERT", {"cxx_constexpr", "c_static_assert"}, {languages = "c++11"}) ``` config.h.in ```c ${define HAS_CONSTEXPR} ${define HAS_CONSEXPR_AND_STATIC_ASSERT} ``` config.h ```c /* #undef HAS_CONSTEXPR */ #define HAS_CONSEXPR_AND_STATIC_ASSERT 1 ``` If the current cxx\_constexpr feature supports it, the HAS\_CONSTEXPR macro will be enabled in config.h. #### Added C/C++ standard support detection After 2.5.8, we continue to add support for cstd and c++ std version detection, related issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) E.g: ```lua configvar_check_features("HAS_CXX_STD_98", "cxx_std_98") configvar_check_features("HAS_CXX_STD_11", "cxx_std_11", {languages = "c++11"}) configvar_check_features("HAS_CXX_STD_14", "cxx_std_14", {languages = "c++14"}) configvar_check_features("HAS_CXX_STD_17", "cxx_std_17", {languages = "c++17"}) configvar_check_features("HAS_CXX_STD_20", "cxx_std_20", {languages = "c++20"}) configvar_check_features("HAS_C_STD_89", "c_std_89") configvar_check_features("HAS_C_STD_99", "c_std_99") configvar_check_features("HAS_C_STD_11", "c_std_11", {languages = "c11"}) configvar_check_features("HAS_C_STD_17", "c_std_17", {languages = "c17"}) ``` #### New compiler built-in macro detection We can also detect the existence of some built-in macro definitions in the compiler, such as `__GNUC__`, etc. We can use the `check_macros` and `configvar_check_macros` auxiliary scripts to detect their existence. Related issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) ```lua - Check whether the macro is defined configvar_check_macros("HAS_GCC", "__GNUC__") - The detection macro is not defined configvar_check_macros("NO_GCC", "__GNUC__", {defined = false}) - Detect macro conditions configvar_check_macros("HAS_CXX20", "__cplusplus >= 202002L", {languages = "c++20"}) ``` ### Added support for Qt 4.x In addition to Qt 5.x and 6.x, we have also added support for some old projects based on Qt 4.x. ### Added support for Android NDK r23 Due to some structural changes made by google to the Android NDK, r23 has affected the support of xmake for some compilation features of the android project. In this version, we have also made a repair. ### Fix the Unicode encoding problem of the vsxmake plugin In addition, if Unicode is used as the project directory, the generated vsxmake project will be affected, causing many problems in the compilation and access of the vs project. We have also fixed it in the new version. ## Changelog ### New features * [#388](https://github.com/xmake-io/xmake/issues/388): Pascal Language Support * [#1682](https://github.com/xmake-io/xmake/issues/1682): Add optional lua5.3 backend instead of luajit to provide better compatibility * [#1622](https://github.com/xmake-io/xmake/issues/1622): Support Swig * [#1714](https://github.com/xmake-io/xmake/issues/1714): Support build local embed cmake projects * [#1715](https://github.com/xmake-io/xmake/issues/1715): Support to detect compiler language standards as features and add `check_macros` * Support Loongarch ### Change * [#1618](https://github.com/xmake-io/xmake/issues/1618): Improve vala to support to generate libraries and bindings * Improve Qt rules to support Qt 4.x * Improve `set_symbols("debug")` to generate pdb file for clang on windows * [#1638](https://github.com/xmake-io/xmake/issues/1638): Improve to merge static library * Improve on\_load/after\_load to support to add target deps dynamically * [#1675](https://github.com/xmake-io/xmake/pull/1675): Rename dynamic and import library suffix for mingw * [#1694](https://github.com/xmake-io/xmake/issues/1694): Support to define a variable without quotes for configuration files * Support Android NDK r23 * Add `c++latest` and `clatest` for `set_languages` * [#1720](https://github.com/xmake-io/xmake/issues/1720): Add `save_scope` and `restore_scope` to fix `check_xxx` apis * [#1726](https://github.com/xmake-io/xmake/issues/1726): Improve compile\_commands generator to support nvcc ### Bugs fixed * [#1671](https://github.com/xmake-io/xmake/issues/1671): Fix incorrect absolute path after installing precompiled packages * [#1689](https://github.com/xmake-io/xmake/issues/1689): Fix unicode chars bug for vsxmake --- --- url: /zh/posts/xmake-update-v2.5.8.md --- ### Pascal 语言支持 目前,我们可以使用跨平台的 Free pascal 工具链 fpc 去编译构建 Pascal 程序,例如: #### 控制台程序 ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.pas") ``` #### 动态库程序 ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.pas") target("test") set_kind("binary") add_deps("foo") add_files("src/main.pas") ``` 我们也可以通过 `add_fcflags()` 接口添加 Pascal 代码相关的编译选项。 更多例子见:[Pascal examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/pascal) ### Vala 库编译支持 上个版本,我们新增了对 Vala 语言的支持,但是之前只能支持控制台程序的编译,无法生成库文件。而这个版本中,我们额外增加了对静态库和动态库的编译支持。 #### 静态库程序 我们能够通过 `add_values("vala.header", "mymath.h")` 设置导出的接口头文件名,通过 `add_values("vala.vapi", "mymath-1.0.vapi")` 设置导出的 vapi 文件名。 ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("static") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` #### 动态库程序 ```lua add_rules("mode.release", "mode.debug") add_requires("glib") target("mymath") set_kind("shared") add_rules("vala") add_files("src/mymath.vala") add_values("vala.header", "mymath.h") add_values("vala.vapi", "mymath-1.0.vapi") add_packages("glib") target("test") set_kind("binary") add_deps("mymath") add_rules("vala") add_files("src/main.vala") add_packages("glib") ``` 更多例子见:[Vala examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/vala) ### Swig 模块支持 我们提供了 `swig.c` 和 `swig.cpp` 规则,可以对指定的脚本语言,调用 swig 程序生成 c/c++ 模块接口代码,然后配合 xmake 的包管理系统实现完全自动化的模块和依赖包整合。 相关 issues: [#1622](https://github.com/xmake-io/xmake/issues/1622) #### Lua/C 模块 ```lua add_rules("mode.release", "mode.debug") add_requires("lua") target("example") add_rules("swig.c", {moduletype = "lua"}) add_files("src/example.i", {swigflags = "-no-old-metatable-bindings"}) add_files("src/example.c") add_packages("lua") ``` 其中,swigflags 可以设置传递一些 swig 特有的 flags 选项。 #### Python/C 模块 ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.c", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.c") add_packages("python") ``` 如果设置了 scriptdir,那么我们执行安装的时候,会将对应模块的 python wrap 脚本安装到指定目录。 #### Python/C++ 模块 ```lua add_rules("mode.release", "mode.debug") add_requires("python 3.x") target("example") add_rules("swig.cpp", {moduletype = "python"}) add_files("src/example.i", {scriptdir = "share"}) add_files("src/example.cpp") add_packages("python") ``` ### Lua5.3 运行时支持 xmake 之前一直使用的 Luajit 作为默认的运行时,因为当初考虑到 Luajit 相对更加快速,并且固定的 lua 5.1 语法更加适合 xmake 内部实现的需要。 但是考虑到 Luajit 的更新不给力,作者维护不是很积极,并且它的跨平台性比较差,对于一些新出的架构,比如:Loongarch,riscv 等支持不及时,这多少限制了 xmake 的平台支持力度。 为此,新版本中,我们也将 Lua5.3 作为可选的运行时内置了进来,我们只需要通过下面的命令编译安装 xmake,就可以从 Luajit 切换到 Lua5.3 运行时: #### Linux/macOS ```bash $ make RUNTIME=lua ``` #### Windows ```bash $ cd core $ xmake f --runtime=lua $ xmake ``` 目前,当前版本还是默认采用的 luajit 运行时,用户可以根据自己的需求切换到 Lua5.3 运行时,但这对于用户的项目 xmake.lua 配置脚本几乎没有任何兼容性影响。 因为 xmake 的配置接口都已经做了一层的抽象封装,一些 Luajit/Lua5.3 存在兼容性差异的原生接口是不会开放给用户使用的,所以对项目构建来说,是完全无感知的。 唯一的区别就是,带有 Lua5.3 的 xmake 支持更多的平台和架构。 #### 性能对比 我做过一些基础构建测试,不管是启动时间,构建性能还是内存占用,Lua5.3 和 Luajit 的 xmake 都几乎没有任何差别。因为对于构建系统,主要的性能瓶颈是在编译器上,自身脚本的损耗占比是非常小的。 而且 xmake 内部的一些底层 Lua 模块,比如 io,字符编码,字符串操作等,都自己用 c 代码全部重写过的,完全不依赖特定的 Lua 运行时引擎。 #### 是否会考虑默认切换到 Lua? 由于我们刚刚支持 Lua5.3,尽管目前测试下来已经比较稳定,但是为了确保用户环境不受到任何影响,我们还需要再观察一段时间,短期还是默认使用 Luajit。 等到 2.6.1 版本开始,我们会全面开始切换到 Lua5.3 作为默认的运行时环境,大家有兴趣的话,也可以线帮忙测试下,如果遇到问题,欢迎到 issues 上反馈。 #### LoongArch 架构支持 由于我们增加了 Lua5.3 运行时支持,所以现在我们已经可以支持再 LoongArch 架构上运行 xmake,并且所有测试例子都已经测试通过。 #### Lua 5.4 目前,我们对 Lua 5.4 还保持观望状态,如果后面等 lua5.4 稳定了,我们也会尝试考虑继续升级到 Lua5.4。 ### 第三方源码混合编译支持 #### 集成 CMake 代码库 新版本中,我们已经能够通过 xmake 的包模式直接集成自己项目中带有 CMakeLists.txt 的代码库,而不是通过远程下载安装。 相关 issues: [#1714](https://github.com/xmake-io/xmake/issues/1714) 例如,我们有如下项目结构: ``` ├── foo │   ├── CMakeLists.txt │   └── src │   ├── foo.c │   └── foo.h ├── src │   └── main.c ├── test.lua └── xmake.lua ``` foo 目录下是一个使用 cmake 维护的静态库,而根目录下使用了 xmake 来维护,我们可以在 xmake.lua 中通过定义 `package("foo")` 包来描述如何构建 foo 代码库。 ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` 其中,我们通过 `set_sourcedir()` 来设置 foo 包的代码目录位置,然后通过 import 导入 `package.tools.cmake` 辅助模块来调用 cmake 构建代码,xmake 会自动获取生成的 libfoo.a 和对应的头文件。 :::注意 如果仅仅本地源码集成,我们不需要额外设置 `add_urls` 和 `add_versions`。 ::: 关于包的配置描述,详情见:[包描述说明](https://xmake.io/zh/) 定义完包后,我们就可以通过 `add_requires("foo")` 和 `add_packages("foo")` 来集成使用它了,就跟集成远程包一样的使用方式。 另外,`on_test` 是可选的,如果想要严格检测包的编译安装是否成功,可以在里面做一些测试。 完整例子见:[Library with CMakeLists](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/library_with_cmakelists) #### 集成 autoconf 代码库 我们也可以使用 `package.tools.autoconf` 来本地集成带有 autoconf 维护的第三方代码库。 ```lua package("pcre2") set_sourcedir(path.join(os.scriptdir(), "3rd/pcre2")) add_configs("jit", {description = "Enable jit.", default = true, type = "boolean"}) add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}}) on_load(function (package) local bitwidth = package:config("bitwidth") or "8" package:add("links", "pcre2-" .. bitwidth) package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth) if not package:config("shared") then package:add("defines", "PCRE2_STATIC") end end) on_install("macosx", "linux", "mingw", function (package) local configs = {} table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) if package:debug() then table.insert(configs, "--enable-debug") end if package:config("pic") ~= false then table.insert(configs, "--with-pic") end if package:config("jit") then table.insert(configs, "--enable-jit") end local bitwidth = package:config("bitwidth") or "8" if bitwidth ~= "8" then table.insert(configs, "--disable-pcre2-8") table.insert(configs, "--enable-pcre2-" .. bitwidth) end import("package.tools.autoconf").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("pcre2_compile", {includes = "pcre2.h"})) end) ``` `package.tools.autoconf` 和 `package.tools.cmake` 模块都是可以支持 mingw/cross/iphoneos/android 等交叉编译平台和工具链的,xmake 会自动传递对应的工具链进去,用户不需要做任何其他事情。 #### 集成其他构建系统 我们还支持集成 Meson/Scons/Make 等其他构建系统维护的代码库,仅仅只需要导入对应的构建辅助模块,这里就不一一细讲了,我们可以进一步查阅文档:[集成本地第三方源码库](https://xmake.io/zh/) ### 改进编译器特性检测 在之前的版本中,我们可以通过 `check_features` 辅助接口来检测指定的编译器特性,比如: ```lua includes("check_features.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_features("HAS_CONSTEXPR", "cxx_constexpr") configvar_check_features("HAS_CONSEXPR_AND_STATIC_ASSERT", {"cxx_constexpr", "c_static_assert"}, {languages = "c++11"}) ``` config.h.in ```c ${define HAS_CONSTEXPR} ${define HAS_CONSEXPR_AND_STATIC_ASSERT} ``` config.h ```c /* #undef HAS_CONSTEXPR */ #define HAS_CONSEXPR_AND_STATIC_ASSERT 1 ``` 如果当前 cxx\_constexpr 特性支持,就会在 config.h 中启用 HAS\_CONSTEXPR 宏。 #### 新增 C/C++ 标准支持检测 2.5.8 之后,我们继续新增了对 cstd 和 c++ std 版本检测支持,相关 issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) 例如: ```lua configvar_check_features("HAS_CXX_STD_98", "cxx_std_98") configvar_check_features("HAS_CXX_STD_11", "cxx_std_11", {languages = "c++11"}) configvar_check_features("HAS_CXX_STD_14", "cxx_std_14", {languages = "c++14"}) configvar_check_features("HAS_CXX_STD_17", "cxx_std_17", {languages = "c++17"}) configvar_check_features("HAS_CXX_STD_20", "cxx_std_20", {languages = "c++20"}) configvar_check_features("HAS_C_STD_89", "c_std_89") configvar_check_features("HAS_C_STD_99", "c_std_99") configvar_check_features("HAS_C_STD_11", "c_std_11", {languages = "c11"}) configvar_check_features("HAS_C_STD_17", "c_std_17", {languages = "c17"}) ``` #### 新增编译器内置宏检测 我们还能检测编译器存在一些内置的宏定义,比如:`__GNUC__` 等,我们可以通过 `check_macros` 和 `configvar_check_macros` 辅助脚本来检测它们是否存在。 相关 issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) ```lua -- 检测宏是否定义 configvar_check_macros("HAS_GCC", "__GNUC__") -- 检测宏没有被定义 configvar_check_macros("NO_GCC", "__GNUC__", {defined = false}) -- 检测宏条件 configvar_check_macros("HAS_CXX20", "__cplusplus >= 202002L", {languages = "c++20"}) ``` ### 增加对 Qt 4.x 的支持 除了 Qt 5.x 和 6.x,我们对于一些基于 Qt 4.x 的老项目,xmake 也增加了支持。 ### 增加对 Android NDK r23 的支持 由于 google 对 Android NDK 的一些结构改动,导致 r23 影响了 xmake 对 android 项目部分编译特性的支持,在这个版本中,我们也做了修复。 ### 修复 vsxmake 插件 Unicode 编码问题 另外,如果基于 Unicode 作为项目目录,那么生成的 vsxmake 项目会收到影响,导致 vs 项目编译和访问上存在很多问题,我们也在新版本中做了修复。 ## 更新内容 ### 新特性 * [#388](https://github.com/xmake-io/xmake/issues/388): Pascal 语言支持,可以使用 fpc 来编译 free pascal * [#1682](https://github.com/xmake-io/xmake/issues/1682): 添加可选的额lua5.3 运行时替代 luajit,提供更好的平台兼容性。 * [#1622](https://github.com/xmake-io/xmake/issues/1622): 支持 Swig * [#1714](https://github.com/xmake-io/xmake/issues/1714): 支持内置 cmake 等第三方项目的混合编译 * [#1715](https://github.com/xmake-io/xmake/issues/1715): 支持探测编译器语言标准特性,并且新增 `check_macros` 检测接口 * xmake 支持在 Loongarch 架构上运行 ### 改进 * [#1618](https://github.com/xmake-io/xmake/issues/1618): 改进 vala 支持构建动态库和静态库程序 * 改进 Qt 规则去支持 Qt 4.x * 改进 `set_symbols("debug")` 支持 clang/windows 生成 pdb 文件 * [#1638](https://github.com/xmake-io/xmake/issues/1638): 改进合并静态库 * 改进 on\_load/after\_load 去支持动态的添加 target deps * [#1675](https://github.com/xmake-io/xmake/pull/1675): 针对 mingw 平台,重命名动态库和导入库文件名后缀 * [#1694](https://github.com/xmake-io/xmake/issues/1694): 支持在 set\_configvar 中定义一个不带引号的字符串变量 * 改进对 Android NDK r23 的支持 * 为 `set_languages` 新增 `c++latest` 和 `clatest` 配置值 * [#1720](https://github.com/xmake-io/xmake/issues/1720): 添加 `save_scope` 和 `restore_scope` 去修复 `check_xxx` 相关接口 * [#1726](https://github.com/xmake-io/xmake/issues/1726): 改进 compile\_commands 生成器去支持 nvcc ### Bugs 修复 * [#1671](https://github.com/xmake-io/xmake/issues/1671): 修复安装预编译包后,\*.cmake 里面的一些不正确的绝对路径 * [#1689](https://github.com/xmake-io/xmake/issues/1689): 修复 vsxmake 插件的 unicode 字符显示和加载问题 --- --- url: /posts/xmake-update-v2.5.9.md --- ### Nimlang project construction Recently, we have added build support for the Nimlang project. For related issues, see: [#1756](https://github.com/xmake-io/xmake/issues/1756) #### Create an empty project We can use the `xmake create` command to create an empty project. ```console xmake create -l nim -t console test xmake create -l nim -t static test xmake create -l nim -t shared test ``` #### Console Program ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/main.nim") ``` ```console $ xmake -v [33%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache -o:b uild/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` #### Static library program ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```console $ xmake -v [33%]: linking.release libfoo.a /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :staticlib --noMain --passC:-DNimMain=NimMain_B6D5BD02 --passC:-DNimMainInner=NimMainInner_B6D5B D02 --passC:-DNimMainModule=NimMainModule_B6D5BD02 --passC:-DPreMain=PreMain_B6D5BD02 --passC:-D PreMainInner=PreMainInner_B6D5BD02 -o:build/macosx/x86_64/release/libfoo.a src/foo.nim [66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` #### Dynamic library program ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```console $ xmake -rv [33%]: linking.release libfoo.dylib /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :lib --noMain -o:build/macosx/x86_64/release/libfoo.dylib src/foo.nim [66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` #### C code mixed compilation ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/*.c") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` #### Nimble dependency package integration For a complete example, see: [Nimble Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/nimble_package) ```lua add_rules("mode.debug", "mode.release") add_requires("nimble::zip >0.3") target("test") set_kind("binary") add_files("src/main.nim") add_packages("nimble::zip") ``` main.nim ```nim import zip/zlib echo zlibVersion() ``` #### Native dependency package integration For a complete example, see: [Native Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/native_package) ```lua add_rules("mode.debug", "mode.release") add_requires("zlib") target("test") set_kind("binary") add_files("src/main.nim") add_packages("zlib") ``` main.nim ```nim proc zlibVersion(): cstring {.cdecl, importc} echo zlibVersion() ``` ### Unity Build acceleration We know that C++ code compilation speed is usually very slow, because each code file needs to parse the imported header file. With Unity Build, we accelerate the compilation of the project by combining multiple cpp files into one. The main benefit is to reduce the repetitive work of parsing and compiling the contents of the header files contained in multiple source files. The contents of the header files are usually It accounts for most of the code in the source file after preprocessing. Unity build also reduces the overhead caused by having a large number of small source files by reducing the number of object files created and processed by the compilation chain, and allows inter-procedural analysis and optimization across files that form a unified build task (similar to optimization during effect linking ). It can greatly improve the compilation speed of C/C++ code, usually by 30%. However, depending on the complexity of the project, the benefits it brings depend on the situation of the project. xmake has also supported this build mode in v2.5.9. For related issues, see [#1019](https://github.com/xmake-io/xmake/issues/1019). #### How to enable? We provide two built-in rules to handle Unity Build for C and C++ code respectively. ```lua add_rules("c.unity_build") add_rules("c++.unity_build") ``` #### Batch mode By default, as long as the above rules are set, Unity Build in Batch mode will be enabled, that is, xmake will automatically organize and merge according to the project code files. ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") ``` We can additionally specify the size of each merged Batch by setting the `{batchsize = 2}` parameter to the rule, which means that every two C++ files are automatically merged and compiled. The compilation effect is roughly as follows: ```console $ xmake -r [11%]: ccache compiling.release build/.gens/test/unity_build/unity_642A245F.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_bar.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_73161A20.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_F905F036.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/unity_foo.cpp [11%]: ccache compiling.release build/.gens/test/unity_build/main.c [77%]: linking.release test [100%]: build ok ``` Since we only enabled the Unity Build of C++, the C code is still compiled one by one normally. In addition, in the Unity Build mode, we can still speed up the parallel compilation as much as possible without conflicting each other. If the `batchsize` parameter is not set, all files will be merged into one file for compilation by default. #### Group Mode If the automatic merging effect of the above Batch mode is not satisfactory, we can also use custom grouping to manually configure which files are merged together to participate in the compilation, which makes users more flexible and controllable. ```lua target("test") set_kind("binary") add_rules("c++.unity_build", {batchsize = 0}) - disable batch mode add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` We use `{unity_group = "foo"}` to specify the name of each group and which files are included. The files in each group will be merged into one code file separately. In addition, `batchsize = 0` also forcibly disables the Batch mode, that is, if there is no unity\_group grouped code files, we will still compile them separately, and will not automatically turn on automatic merging. #### Batch and Group mixed mode As long as we change the above `batchsize = 0` to a value other than 0, we can let the remaining code files continue to open the Batch mode in the grouping mode to automatically merge and compile. ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` #### Ignore the specified file If it is in Batch mode, because it is an automatic merge operation, all files will be merged by default, but if some code files do not want to participate in the merge, then we can also ignore them through `{unity_ignored = true}`. ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/test/*.c", {unity_ignored = true}) - ignore these files ``` #### Unique ID Although the benefits of Unity Build are good, we still encounter some unexpected situations. For example, in our two code files, under the global namespace, there are global variables and functions with the same name. Then, merge compilation will bring about compilation conflicts, and the compiler usually reports global variable redefinition errors. In order to solve this problem, we need to make some modifications to the user code, and then cooperate with the build tool to solve it. For example, our foo.cpp and bar.cpp both have global variable i. foo.cpp ```c namespace { int i = 42; } int foo() { return i; } ``` bar.cpp ```c namespace { int i = 42; } int bar() { return i; } ``` Then, our merge compilation will conflict, and we can introduce a Unique ID to isolate the global anonymous space. foo.cpp ```c namespace MY_UNITY_ID { int i = 42; } int foo() { return MY_UNITY_ID::i; } ``` bar.cpp ```c namespace MY_UNITY_ID { int i = 42; } int bar() { return MY_UNITY_ID::i; } ``` Next, we also need to ensure that after the code is merged, the definitions of `MY_UNITY_ID` in foo and bar are completely different, and a unique ID value can be calculated according to the file name, which does not conflict with each other, which is to achieve the following merge effect: ```c #define MY_UNITY_ID #include "foo.c" #undef MY_UNITY_ID #define MY_UNITY_ID #include "bar.c" #undef MY_UNITY_ID ``` This may seem troublesome, but the user does not need to care about these, xmake will automatically process them when merging, the user only needs to specify the name of the Unique ID, for example, the following: ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2, uniqueid = "MY_UNITY_ID"}) add_files("src/*.c", "src/*.cpp") ``` Dealing with global variables, as well as global macro definitions with the same name, functions, etc., can be used in this way to avoid conflicts. ### C++20 Modules xmake uses `.mpp` as the default module extension, but also supports `.ixx`, `.cppm`, `.mxx` and other extensions. In the early days, xmake experimentally supported C++ Modules TS, but at that time, gcc could not support it well, and the dependencies between modules were not supported either. Recently, we have made a lot of improvements to xmake. We have fully supported the C++20 Modules construction support of gcc-11/clang/msvc, and can automatically analyze the dependencies between modules to maximize parallel compilation. At the same time, the new version of clang/msvc has also been better handled. ```lua set_languages("c++20") target("test") set_kind("binary") add_files("src/*.cpp", "src/*.mpp") ``` For more examples, see: [C++ Modules](https://github.com/xmake-io/xmake/tree/master/tests/projects/c%2B%2B/modules) ### Lua5.4 runtime support In the last version, we added support for the Lua5.3 runtime. In this version, we further upgraded the Lua runtime to 5.4. Compared with 5.3, the runtime performance and memory utilization have been greatly improved. However, the current default runtime of xmake is still luajit, and it is expected that version 2.6.1 (that is, the next version) will officially switch to Lua5.4 as the default runtime. Although the Lua runtime is switched, it is completely unaware to the user side and fully compatible with the existing project configuration, because xmake originally provides a layer of encapsulation for the exposed api. The interfaces that have compatibility issues between Lua versions, such as setfenv, ffi, etc., are hidden internally and are not exposed to users. ### Keil MDK tool chain support In this version, we also added Keil/MDK embedded compilation tool chain support, related example projects: \[Example]\(https://github.com/xmake-io/xmake/tree/dev/tests/projects /mdk/hello) xmake will automatically detect the compiler installed by Keil/MDK, related issues [#1753](https://github.com/xmake-io/xmake/issues/1753). #### Compile with armcc ```console $ xmake f -p cross -a cortex-m3 --toolchain=armcc -c $ xmake ``` #### Compile with armclang ```console $ xmake f -p cross -a cortex-m3 --toolchain=armclang -c $ xmake ``` #### Console Program ```lua target("hello") add_deps("foo") add_rules("mdk.console") add_files("src/*.c", "src/*.s") add_defines("__EVAL", "__MICROLIB") add_includedirs("src/lib/cmsis") ``` #### Static library program ```lua add_rules("mode.debug", "mode.release") target("foo") add_rules("mdk.static") add_files("src/foo/*.c") ``` ### Wasi toolchain support We previously supported the emcc tool chain of the wasm platform to build the wasm program, and here, we added another Wasm tool chain with WASI enabled to replace emcc. ```console $ xmake f -p wasm --toolchain=wasi $ xmake ``` ### Circle toolchain support We also added support for the circle compiler, which is a new C++20 compiler with some interesting compile-time meta-programming features. Those who are interested can check it out on the official website: https://www.circle -lang.org/ ```console $ xmake f --toolchain=circle $ xmake ``` ### gcc-8/9/10/11 specific version support If the user additionally installs a specific version of the gcc tool chain such as gcc-11, gcc-10, the local gcc program may be named `/usr/bin/gcc-11`. One way is to switch by specifying the configuration one by one through `xmake f --cc=gcc-11 --cxx=gcc-11 --ld=g++-11`, but it is very cumbersome. Therefore, xmake also provides a faster switching method: ```console $ xmake f --toolchain=gcc-11 -c $ xmake ``` You only need to specify the version name corresponding to `gcc-11` to quickly switch the entire gcc tool chain. ### C++17/20 Compiler feature detection xmake provides the check\_features auxiliary interface to detect compiler features. ```lua includes("check_features.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_features("HAS_CONSTEXPR", "cxx_constexpr") configvar_check_features("HAS_CONSEXPR_AND_STATIC_ASSERT", {"cxx_constexpr", "c_static_assert"}, {languages ​​= "c++11"}) ``` config.h.in ```c ${define HAS_CONSTEXPR} ${define HAS_CONSEXPR_AND_STATIC_ASSERT} ``` config.h ```c /* #undef HAS_CONSTEXPR */ #define HAS_CONSEXPR_AND_STATIC_ASSERT 1 ``` In version 2.5.9, we added c++17 feature detection: | Feature name | | ------------------------------------ | | cxx\_aggregate\_bases | | cxx\_aligned\_new | | cxx\_capture\_star\_this | | cxx\_constexpr | | cxx\_deduction\_guides | | cxx\_enumerator\_attributes | | cxx\_fold\_expressions | | cxx\_guaranteed\_copy\_elision | | cxx\_hex\_float | | cxx\_if\_constexpr | | cxx\_inheriting\_constructors | | cxx\_inline\_variables | | cxx\_namespace\_attributes | | cxx\_noexcept\_function\_type | | cxx\_nontype\_template\_args | | cxx\_nontype\_template\_parameter\_auto | | cxx\_range\_based\_for | | cxx\_static\_assert | | cxx\_structured\_bindings | | cxx\_template\_template\_args | | cxx\_variadic\_using | Also added c++20 feature detection: | Feature name | | ------------------------------------ | | cxx\_aggregate\_paren\_init | | cxx\_char8\_t | | cxx\_concepts | | cxx\_conditional\_explicit | | cxx\_consteval | | cxx\_constexpr | | cxx\_constexpr\_dynamic\_alloc | | cxx\_constexpr\_in\_decltype | | cxx\_constinit | | cxx\_deduction\_guides | | cxx\_designated\_initializers | | cxx\_generic\_lambdas | | cxx\_impl\_coroutine | | cxx\_impl\_destroying\_delete | | cxx\_impl\_three\_way\_comparison | | cxx\_init\_captures | | cxx\_modules | | cxx\_nontype\_template\_args | | cxx\_using\_enum | ### Xrepo package virtual environment management #### Enter the virtual environment The xrepo package management tool that comes with xmake can now well support package virtual machine environment management, similar to nixos' nixpkgs. We can customize some package configurations by adding the xmake.lua file in the current directory, and then enter the specific package virtual environment. ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```console $ xrepo env shell > python --version > luajit --version ``` We can also configure and load the corresponding toolchain environment in xmake.lua, for example, load the VS compilation environment. ```lua set_toolchains("msvc") ``` #### Manage virtual environments We can use the following command to register the specified virtual environment configuration globally to the system for quick switching. ```console $ xrepo env --add /tmp/base.lua ``` At this time, we have saved a global virtual environment called base, and we can view it through the list command. ```console $ xrepo env --list /Users/ruki/.xmake/envs: -base envs(1) found! ``` We can also delete it. ```console $ xrepo env --remove base ``` #### Switch global virtual environment If we register multiple virtual environments, we can also switch them quickly. ```console $ xrepo env -b base shell > python --version ``` Or directly load the specified virtual environment to run specific commands ```console $ xrepo env -b base python --version ``` `xrepo env -b/--bind` is to bind the specified virtual environment. For more details, see: [#1762](https://github.com/xmake-io/xmake/issues/1762) ### Header Only Target Type For the target, we added the `headeronly` target type. For this type of target program, we will not actually compile them because it has no source files to be compiled. But it contains a list of header files, which are usually used for the installation of headeronly library projects, the generation of file lists for IDE projects, and the generation of cmake/pkgconfig import files during the installation phase. E.g: ```lua add_rules("mode.release", "mode.debug") target("foo") set_kind("headeronly") add_headerfiles("src/foo.h") add_rules("utils.install.cmake_importfiles") add_rules("utils.install.pkgconfig_importfiles") ``` For more details, please see: [#1747](https://github.com/xmake-io/xmake/issues/1747) ### Find packages from CMake Now cmake is the de facto standard, so the find\_package provided by CMake can already find a large number of libraries and modules. We fully reuse this part of cmake's ecology to expand xmake's integration of packages. We can use `find_package("cmake::xxx")` to find some packages with cmake, xmake will automatically generate a cmake script to call cmake's find\_package to find some packages and get the bread information. E.g: ```console $ xmake l find_package cmake::ZLIB { links = { "z" }, includedirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/include" }, linkdirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/lib" } } $ xmake l find_package cmake::LibXml2 { links = { "xml2" }, includedirs = { "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/libxml2" }, linkdirs = { "/usr/lib" } } ``` #### Specify version ```lua find_package("cmake::OpenCV", {required_version = "4.1.1"}) ``` #### Specified components ```lua find_package("cmake::Boost", {components = {"regex", "system"}}) ``` #### Default switch ```lua find_package("cmake::Boost", {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}) set(Boost_USE_STATIC_LIB ON) - will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system) ``` #### Set environment variables ```lua find_package("cmake::OpenCV", {envs = {CMAKE_PREFIX_PATH = "xxx"}}) ``` #### Specify custom FindFoo.cmake module script directory mydir/cmake\_modules/FindFoo.cmake ```lua find_package("cmake::Foo", {moduledirs = "mydir/cmake_modules"}) ``` #### Package dependency integration ```lua package("xxx") on_fetch(function (package, opt) return package:find_package("cmake::xxx", opt) end) package_end() add_requires("xxx") ``` #### Package dependency integration (optional component) ```lua package("boost") add_configs("regex", {description = "Enable regex.", default = false, type = "boolean"}) on_fetch(function (package, opt) opt.components = {} if package:config("regex") then table.insert(opt.components, "regex") end return package:find_package("cmake::Boost", opt) end) package_end() add_requires("boost", {configs = {regex = true}}) ``` Related issues: [#1632](https://github.com/xmake-io/xmake/issues/1632) ### Add custom commands to CMakelists.txt We have further improved the cmake generator. Now you can serialize the custom script in rule into a list of commands and generate them together to CMakelists.txt However, currently only the serialization of batchcmds series scripts can be supported. ```lua rule("foo") after_buildcmd(function (target, batchcmds, opt) batchcmds:show("hello xmake!") batchcmds:cp("xmake.lua", "/tmp/") - batchcmds:execv("echo", {"hello", "world!"}) - batchcmds:runv("echo", {"hello", "world!"}) end) target("test") set_kind("binary") add_rules("foo") add_files("src/*.c") ``` It will generate CMakelists.txt similar to the following ``` # ... add_custom_command(TARGET test POST_BUILD COMMAND echo hello xmake! VERBATIM ) add_custom_command(TARGET test POST_BUILD COMMAND cp xmake.lua /tmp/ VERBATIM ) target_sources(test PRIVATE src/main.c ) ``` However, the actual effect of cmake's `ADD_CUSTOM_COMMAND` PRE\_BUILD differs greatly on different generators, which cannot meet our needs, so we have done a lot of processing to support it. Related issues: [#1735](https://github.com/xmake-io/xmake/issues/1735) ## Changelog ### New features * [#1736](https://github.com/xmake-io/xmake/issues/1736): Support wasi-sdk toolchain * Support Lua 5.4 runtime * Add gcc-8, gcc-9, gcc-10, gcc-11 toolchains * [#1623](https://github.com/xmake-io/xmake/issues/1632): Support find\_package from cmake * [#1747](https://github.com/xmake-io/xmake/issues/1747): Add `set_kind("headeronly")` for target to install files for headeronly library * [#1019](https://github.com/xmake-io/xmake/issues/1019): Support Unity build * [#1438](https://github.com/xmake-io/xmake/issues/1438): Support code amalgamation, `xmake l cli.amalgamate` * [#1765](https://github.com/xmake-io/xmake/issues/1756): Support nim language * [#1762](https://github.com/xmake-io/xmake/issues/1762): Manage and switch the given package envs for `xrepo env` * [#1767](https://github.com/xmake-io/xmake/issues/1767): Support Circle compiler * [#1753](https://github.com/xmake-io/xmake/issues/1753): Support armcc/armclang toolchains for Keil/MDK * [#1774](https://github.com/xmake-io/xmake/issues/1774): Add table.contains api * [#1735](https://github.com/xmake-io/xmake/issues/1735): Add custom command in cmake generator ### Changes * [#1528](https://github.com/xmake-io/xmake/issues/1528): Check c++17/20 features * [#1729](https://github.com/xmake-io/xmake/issues/1729): Improve C++20 modules for clang/gcc/msvc, support inter-module dependency compilation and parallel optimization * [#1779](https://github.com/xmake-io/xmake/issues/1779): Remove builtin `-Gd` for ml.exe/x86 * [#1781](https://github.com/xmake-io/xmake/issues/1781): Improve get.sh installation script to support nixos --- --- url: /zh/posts/xmake-update-v2.5.9.md --- ## 新特性介绍 ### Nimlang 项目构建 最近,我们新增了对 Nimlang 项目的构建支持,相关 issues 见:[#1756](https://github.com/xmake-io/xmake/issues/1756) #### 创建空工程 我们可以使用 `xmake create` 命令创建空工程。 ```console xmake create -l nim -t console test xmake create -l nim -t static test xmake create -l nim -t shared test ``` #### 控制台程序 ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/main.nim") ``` ```console $ xmake -v [ 33%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache -o:b uild/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` #### 静态库程序 ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```console $ xmake -v [ 33%]: linking.release libfoo.a /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :staticlib --noMain --passC:-DNimMain=NimMain_B6D5BD02 --passC:-DNimMainInner=NimMainInner_B6D5B D02 --passC:-DNimMainModule=NimMainModule_B6D5BD02 --passC:-DPreMain=PreMain_B6D5BD02 --passC:-D PreMainInner=PreMainInner_B6D5BD02 -o:build/macosx/x86_64/release/libfoo.a src/foo.nim [ 66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` #### 动态库程序 ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("shared") add_files("src/foo.nim") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` ```console $ xmake -rv [ 33%]: linking.release libfoo.dylib /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/foo/macosx/x86_64/release/nimcache --app :lib --noMain -o:build/macosx/x86_64/release/libfoo.dylib src/foo.nim [ 66%]: linking.release test /usr/local/bin/nim c --opt:speed --nimcache:build/.gens/test/macosx/x86_64/release/nimcache --pa ssL:-Lbuild/macosx/x86_64/release --passL:-lfoo -o:build/macosx/x86_64/release/test src/main.nim [100%]: build ok! ``` #### C 代码混合编译 ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/*.c") target("test") set_kind("binary") add_deps("foo") add_files("src/main.nim") ``` #### Nimble 依赖包集成 完整例子见:[Nimble Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/nimble_package) ```lua add_rules("mode.debug", "mode.release") add_requires("nimble::zip >0.3") target("test") set_kind("binary") add_files("src/main.nim") add_packages("nimble::zip") ``` main.nim ```nim import zip/zlib echo zlibVersion() ``` #### Native 依赖包集成 完整例子见:[Native Package Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/nim/native_package) ```lua add_rules("mode.debug", "mode.release") add_requires("zlib") target("test") set_kind("binary") add_files("src/main.nim") add_packages("zlib") ``` main.nim ```nim proc zlibVersion(): cstring {.cdecl, importc} echo zlibVersion() ``` ### Unity Build 加速 我们知道,C++ 代码编译速度通常很慢,因为每个代码文件都需要解析引入的头文件。 而通过 Unity Build,我们通过将多个 cpp 文件组合成一个来加速项目的编译,其主要好处是减少了解析和编译包含在多个源文件中的头文件内容的重复工作,头文件的内容通常占预处理后源文件中的大部分代码。 Unity 构建还通过减少编译链创建和处理的目标文件的数量来减轻由于拥有大量小源文件而导致的开销,并允许跨形成统一构建任务的文件进行过程间分析和优化(类似于效果链接时优化)。 它可以极大提升 C/C++ 代码的编译速度,通常会有 30% 的速度提升,不过根据项目的复杂程度不同,其带来的效益还是要根据自身项目情况而定。 xmake 在 v2.5.9 版本中,也已经支持了这种构建模式。相关 issues 见 [#1019](https://github.com/xmake-io/xmake/issues/1019)。 #### 如何启用? 我们提供了两个内置规则,分别处理对 C 和 C++ 代码的 Unity Build。 ```lua add_rules("c.unity_build") add_rules("c++.unity_build") ``` #### Batch 模式 默认情况下,只要设置上述规则,就会启用 Batch 模式的 Unity Build,也就是 xmake 自动根据项目代码文件,自动组织合并。 ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") ``` 我们可以额外通过设置 `{batchsize = 2}` 参数到规则,来指定每个合并 Batch 的大小数量,这里也就是每两个 C++ 文件自动合并编译。 编译效果大概如下: ```console $ xmake -r [ 11%]: ccache compiling.release build/.gens/test/unity_build/unity_642A245F.cpp [ 11%]: ccache compiling.release build/.gens/test/unity_build/unity_bar.cpp [ 11%]: ccache compiling.release build/.gens/test/unity_build/unity_73161A20.cpp [ 11%]: ccache compiling.release build/.gens/test/unity_build/unity_F905F036.cpp [ 11%]: ccache compiling.release build/.gens/test/unity_build/unity_foo.cpp [ 11%]: ccache compiling.release build/.gens/test/unity_build/main.c [ 77%]: linking.release test [100%]: build ok ``` 由于我们仅仅启用了 C++ 的 Unity Build,所以 C 代码还是正常挨个编译。另外在 Unity Build 模式下,我们还是可以做到尽可能的并行编译加速,互不冲突。 如果没有设置 `batchsize` 参数,那么默认会吧所有文件合并到一个文件中进行编译。 #### Group 模式 如果上面的 Batch 模式自动合并效果不理想,我们也可以使用自定义分组,来手动配置哪些文件合并到一起参与编译,这使得用户更加地灵活可控。 ```lua target("test") set_kind("binary") add_rules("c++.unity_build", {batchsize = 0}) -- disable batch mode add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` 我们使用 `{unity_group = "foo"}` 来指定每个分组的名字,以及包含了哪些文件,每个分组的文件都会单独被合并到一个代码文件中去。 另外,`batchsize = 0` 也强行禁用了 Batch 模式,也就是说,没有设置 unity\_group 分组的代码文件,我们还是会单独编译它们,也不会自动开启自动合并。 #### Batch 和 Group 混合模式 我们只要把上面的 `batchsize = 0` 改成非 0 值,就可以让分组模式下,剩余的代码文件继续开启 Batch 模式自动合并编译。 ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/foo/*.c", {unity_group = "foo"}) add_files("src/bar/*.c", {unity_group = "bar"}) ``` #### 忽略指定文件 如果是 Batch 模式下,由于是自动合并操作,所以默认会对所有文件执行合并,但如果有些代码文件我们不想让它参与合并,那么我们也可以通过 `{unity_ignored = true}` 去忽略它们。 ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2}) add_files("src/*.c", "src/*.cpp") add_files("src/test/*.c", {unity_ignored = true}) -- ignore these files ``` #### Unique ID 尽管 Unity Build 带啦的收益不错,但是我们还是会遇到一些意外的情况,比如我们的两个代码文件里面,全局命名空间下,都存在相同名字的全局变量和函数。 那么,合并编译就会带来编译冲突问题,编译器通常会报全局变量重定义错误。 为了解决这个问题,我们需要用户代码上做一些修改,然后配合构建工具来解决。 比如,我们的 foo.cpp 和 bar.cpp 都有全局变量 i。 foo.cpp ```c namespace { int i = 42; } int foo() { return i; } ``` bar.cpp ```c namespace { int i = 42; } int bar() { return i; } ``` 那么,我们合并编译就会冲突,我们可以引入一个 Unique ID 来隔离全局的匿名空间。 foo.cpp ```c namespace MY_UNITY_ID { int i = 42; } int foo() { return MY_UNITY_ID::i; } ``` bar.cpp ```c namespace MY_UNITY_ID { int i = 42; } int bar() { return MY_UNITY_ID::i; } ``` 接下来,我们还需要保证代码合并后, `MY_UNITY_ID` 在 foo 和 bar 中的定义完全不同,可以按文件名算一个唯一 ID 值出来,互不冲突,也就是实现下面的合并效果: ```c #define MY_UNITY_ID #include "foo.c" #undef MY_UNITY_ID #define MY_UNITY_ID #include "bar.c" #undef MY_UNITY_ID ``` 这看上去似乎很麻烦,但是用户不需要关心这些,xmake 会在合并时候自动处理它们,用户只需要指定这个 Unique ID 的名字就行了,例如下面这样: ```lua target("test") set_kind("binary") add_includedirs("src") add_rules("c++.unity_build", {batchsize = 2, uniqueid = "MY_UNITY_ID"}) add_files("src/*.c", "src/*.cpp") ``` 处理全局变量,还有全局的重名宏定义,函数什么的,都可以采用这种方式来避免冲突。 ### C++20 Modules xmake 采用 `.mpp` 作为默认的模块扩展名,但是也同时支持 `.ixx`, `.cppm`, `.mxx` 等扩展名。 早期,xmake 试验性支持过 C++ Modules TS,但是那个时候,gcc 还不能很好的支持,并且模块间的依赖也不支持。 最近,我们对 xmake 做了大量改进,已经完整支持 gcc-11/clang/msvc 的 C++20 Modules 构建支持,并且能够自动分析模块间的依赖关系,实现最大化并行编译。 同时,对新版本的 clang/msvc 也做了更好地处理。 ```lua set_languages("c++20") target("test") set_kind("binary") add_files("src/*.cpp", "src/*.mpp") ``` 更多例子见:[C++ Modules](https://github.com/xmake-io/xmake/tree/master/tests/projects/c%2B%2B/modules) ### Lua5.4 运行时支持 上个版本,我们增加了对 Lua5.3 运行时支持,而在这个版本中,我们进一步升级 Lua 运行时到 5.4,相比 5.3,运行性能和内存利用率上都有很大的提升。 不过,目前 xmake 的默认运行时还是 luajit,预计 2.6.1 版本(也就是下个版本),会正式切到 Lua5.4 作为默认的运行时。 尽管切换了 Lua 运行时,但是对于用户端,完全是无感知的,并且完全兼容现有工程配置,因为 xmake 原本就对暴露的 api 提供了一层封装, 对于 lua 版本之间存在兼容性问题的接口,例如 setfenv, ffi 等都隐藏在内部,原本就没有暴露给用户使用。 ### Keil MDK 工具链支持 我们在这个版本中,还新增了 Keil/MDK 嵌入式编译工具链的支持,相关例子工程:[Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/mdk/hello) xmake 会自动探测 Keil/MDK 安装的编译器,相关 issues [#1753](https://github.com/xmake-io/xmake/issues/1753)。 #### 使用 armcc 编译 ```console $ xmake f -p cross -a cortex-m3 --toolchain=armcc -c $ xmake ``` #### 使用 armclang 编译 ```console $ xmake f -p cross -a cortex-m3 --toolchain=armclang -c $ xmake ``` #### 控制台程序 ```lua target("hello") add_deps("foo") add_rules("mdk.console") add_files("src/*.c", "src/*.s") add_defines("__EVAL", "__MICROLIB") add_includedirs("src/lib/cmsis") ``` #### 静态库程序 ```lua add_rules("mode.debug", "mode.release") target("foo") add_rules("mdk.static") add_files("src/foo/*.c") ``` ### Wasi 工具链支持 之前我们支持了 wasm 平台的 emcc 工具链来构建 wasm 程序,而这里,我们新加了另外一个启用了 WASI 的 Wasm 工具链来替换 emcc。 ```console $ xmake f -p wasm --toolchain=wasi $ xmake ``` ### Circle 工具链支持 我们还新增了 circle 编译器的支持,这是个新的 C++20 编译器,额外附带了一些有趣的编译期元编程特性,有兴趣的同学可以到官网查看:https://www.circle-lang.org/ ```console $ xmake f --toolchain=circle $ xmake ``` ### gcc-8/9/10/11 特定版本支持 如果用户额外安装了 gcc-11, gcc-10 等特定版本的 gcc 工具链,在本地的 gcc 程序命名可能是 `/usr/bin/gcc-11`。 一种办法是通过 `xmake f --cc=gcc-11 --cxx=gcc-11 --ld=g++-11` 挨个指定配置来切换,但非常繁琐。 所以,xmake 也提供了更加快捷的切换方式: ```console $ xmake f --toolchain=gcc-11 -c $ xmake ``` 只需要指定 `gcc-11` 对应的版本名,就可以快速切换整个 gcc 工具链。 ### C++17/20 编译器特性检测 xmake 提供了 check\_features 辅助接口来检测编译器特性。 ```lua includes("check_features.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_features("HAS_CONSTEXPR", "cxx_constexpr") configvar_check_features("HAS_CONSEXPR_AND_STATIC_ASSERT", {"cxx_constexpr", "c_static_assert"}, {languages = "c++11"}) ``` config.h.in ```c ${define HAS_CONSTEXPR} ${define HAS_CONSEXPR_AND_STATIC_ASSERT} ``` config.h ```c /* #undef HAS_CONSTEXPR */ #define HAS_CONSEXPR_AND_STATIC_ASSERT 1 ``` 而在 2.5.9 版本中,我们新增了 c++17 特性检测: | 特性名 | | ------------------------------------ | | cxx\_aggregate\_bases | | cxx\_aligned\_new | | cxx\_capture\_star\_this | | cxx\_constexpr | | cxx\_deduction\_guides | | cxx\_enumerator\_attributes | | cxx\_fold\_expressions | | cxx\_guaranteed\_copy\_elision | | cxx\_hex\_float | | cxx\_if\_constexpr | | cxx\_inheriting\_constructors | | cxx\_inline\_variables | | cxx\_namespace\_attributes | | cxx\_noexcept\_function\_type | | cxx\_nontype\_template\_args | | cxx\_nontype\_template\_parameter\_auto | | cxx\_range\_based\_for | | cxx\_static\_assert | | cxx\_structured\_bindings | | cxx\_template\_template\_args | | cxx\_variadic\_using | 还新增了 c++20 特性检测: | 特性名 | | ------------------------------------ | | cxx\_aggregate\_paren\_init | | cxx\_char8\_t | | cxx\_concepts | | cxx\_conditional\_explicit | | cxx\_consteval | | cxx\_constexpr | | cxx\_constexpr\_dynamic\_alloc | | cxx\_constexpr\_in\_decltype | | cxx\_constinit | | cxx\_deduction\_guides | | cxx\_designated\_initializers | | cxx\_generic\_lambdas | | cxx\_impl\_coroutine | | cxx\_impl\_destroying\_delete | | cxx\_impl\_three\_way\_comparison | | cxx\_init\_captures | | cxx\_modules | | cxx\_nontype\_template\_args | | cxx\_using\_enum | ### Xrepo 包虚拟环境管理 #### 进入虚拟环境 xmake 自带的 xrepo 包管理工具,现在已经可以很好的支持包虚拟机环境管理,类似 nixos 的 nixpkgs。 我们可以通过在当前目录下,添加 xmake.lua 文件,定制化一些包配置,然后进入特定的包虚拟环境。 ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```console $ xrepo env shell > python --version > luajit --version ``` 我们也可以在 xmake.lua 配置加载对应的工具链环境,比如加载 vs 的编译环境。 ```lua set_toolchains("msvc") ``` #### 管理虚拟环境 我们可以使用下面的命令,把指定的虚拟环境配置全局注册到系统中,方便快速切换。 ```console $ xrepo env --add /tmp/base.lua ``` 这个时候,我们就保存了一个名叫 base 的全局虚拟环境,我们可以通过 list 命令去查看它。 ```console $ xrepo env --list /Users/ruki/.xmake/envs: - base envs(1) found! ``` 我们也可以删除它。 ```console $ xrepo env --remove base ``` #### 切换全局虚拟环境 如果我们注册了多个虚拟环境,我们也可以快速切换它们。 ```console $ xrepo env -b base shell > python --version ``` 或者直接加载指定虚拟环境运行特定命令 ```console $ xrepo env -b base python --version ``` `xrepo env -b/--bind` 就是绑定指定的虚拟环境,更多详情见:[#1762](https://github.com/xmake-io/xmake/issues/1762) ### Header Only 目标类型 对于 target,我们新增了 `headeronly` 目标类型,这个类型的目标程序,我们不会实际编译它们,因为它没有源文件需要被编译。 但是它包含了头文件列表,这通常用于 headeronly 库项目的安装,IDE 工程的文件列表生成,以及安装阶段的 cmake/pkgconfig 导入文件的生成。 例如: ```lua add_rules("mode.release", "mode.debug") target("foo") set_kind("headeronly") add_headerfiles("src/foo.h") add_rules("utils.install.cmake_importfiles") add_rules("utils.install.pkgconfig_importfiles") ``` 更多详情见:[#1747](https://github.com/xmake-io/xmake/issues/1747) ### 从 CMake 中查找包 现在 cmake 已经是事实上的标准,所以 CMake 提供的 find\_package 已经可以查找大量的库和模块,我们完全复用 cmake 的这部分生态来扩充 xmake 对包的集成。 我们可以通过 `find_package("cmake::xxx")` 去借助 cmake 来找一些包,xmake 会自动生成一个 cmake 脚本来调用 cmake 的 find\_package 去查找一些包,获取里面包信息。 例如: ```console $ xmake l find_package cmake::ZLIB { links = { "z" }, includedirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/include" }, linkdirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/lib" } } $ xmake l find_package cmake::LibXml2 { links = { "xml2" }, includedirs = { "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/libxml2" }, linkdirs = { "/usr/lib" } } ``` #### 指定版本 ```lua find_package("cmake::OpenCV", {required_version = "4.1.1"}) ``` #### 指定组件 ```lua find_package("cmake::Boost", {components = {"regex", "system"}}) ``` #### 预设开关 ```lua find_package("cmake::Boost", {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}) set(Boost_USE_STATIC_LIB ON) -- will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system) ``` #### 设置环境变量 ```lua find_package("cmake::OpenCV", {envs = {CMAKE_PREFIX_PATH = "xxx"}}) ``` #### 指定自定义 FindFoo.cmake 模块脚本目录 mydir/cmake\_modules/FindFoo.cmake ```lua find_package("cmake::Foo", {moduledirs = "mydir/cmake_modules"}) ``` #### 包依赖集成 ```lua package("xxx") on_fetch(function (package, opt) return package:find_package("cmake::xxx", opt) end) package_end() add_requires("xxx") ``` #### 包依赖集成(可选组件) ```lua package("boost") add_configs("regex", { description = "Enable regex.", default = false, type = "boolean"}) on_fetch(function (package, opt) opt.components = {} if package:config("regex") then table.insert(opt.components, "regex") end return package:find_package("cmake::Boost", opt) end) package_end() add_requires("boost", {configs = {regex = true}}) ``` 相关 issues: [#1632](https://github.com/xmake-io/xmake/issues/1632) ### 添加自定义命令到 CMakelists.txt 我们进一步改进了 cmake 生成器,现在可以将 rule 里面自定义的脚本序列化成命令列表,一起生成到 CMakelists.txt 不过目前只能支持 batchcmds 系列脚本的序列化。 ```lua rule("foo") after_buildcmd(function (target, batchcmds, opt) batchcmds:show("hello xmake!") batchcmds:cp("xmake.lua", "/tmp/") -- batchcmds:execv("echo", {"hello", "world!"}) -- batchcmds:runv("echo", {"hello", "world!"}) end) target("test") set_kind("binary") add_rules("foo") add_files("src/*.c") ``` 它将会生成类似如下的 CMakelists.txt ``` # ... add_custom_command(TARGET test POST_BUILD COMMAND echo hello xmake! VERBATIM ) add_custom_command(TARGET test POST_BUILD COMMAND cp xmake.lua /tmp/ VERBATIM ) target_sources(test PRIVATE src/main.c ) ``` 不过 cmake 的 `ADD_CUSTOM_COMMAND` PRE\_BUILD 实际效果在不同生成器上,差异比较大,无法满足我们的需求,因此我们做了很多处理来支持它。 相关 issues: [#1735](https://github.com/xmake-io/xmake/issues/1735) ### 改进对 NixOS 的安装支持 我们还改进了 get.sh 安装脚本,来更好地支持 nixOS。 ## 更新内容 ### 新特性 * [#1736](https://github.com/xmake-io/xmake/issues/1736): 支持 wasi-sdk 工具链 * 支持 Lua 5.4 运行时 * 添加 gcc-8, gcc-9, gcc-10, gcc-11 工具链 * [#1623](https://github.com/xmake-io/xmake/issues/1632): 支持 find\_package 从 cmake 查找包 * [#1747](https://github.com/xmake-io/xmake/issues/1747): 添加 `set_kind("headeronly")` 更好的处理 headeronly 库的安装 * [#1019](https://github.com/xmake-io/xmake/issues/1019): 支持 Unity build * [#1438](https://github.com/xmake-io/xmake/issues/1438): 增加 `xmake l cli.amalgamate` 命令支持代码合并 * [#1765](https://github.com/xmake-io/xmake/issues/1756): 支持 nim 语言 * [#1762](https://github.com/xmake-io/xmake/issues/1762): 为 `xrepo env` 管理和切换指定的环境配置 * [#1767](https://github.com/xmake-io/xmake/issues/1767): 支持 Circle 编译器 * [#1753](https://github.com/xmake-io/xmake/issues/1753): 支持 Keil/MDK 的 armcc/armclang 工具链 * [#1774](https://github.com/xmake-io/xmake/issues/1774): 添加 table.contains api * [#1735](https://github.com/xmake-io/xmake/issues/1735): 添加自定义命令到 cmake 生成器 * [#1781](https://github.com/xmake-io/xmake/issues/1781): 改进 get.sh 安装脚本支持 nixos ### 改进 * [#1528](https://github.com/xmake-io/xmake/issues/1528): 检测 c++17/20 特性 * [#1729](https://github.com/xmake-io/xmake/issues/1729): 改进 C++20 modules 对 clang/gcc/msvc 的支持,支持模块间依赖编译和并行优化 * [#1779](https://github.com/xmake-io/xmake/issues/1779): 改进 ml.exe/x86,移除内置的 `-Gd` 选项 --- --- url: /posts/xmake-update-v2.6.1.md --- ### Switch to Lua5.4 runtime by default After several versions of iterative testing, we officially switched to the Lua5.4 runtime in version 2.6.1. However, this is completely unaware to users, and basically there is no compatibility problem, because xmake encapsulates most of the interfaces, which completely eliminates the compatibility problem between Lua versions. In terms of build performance, because the performance bottleneck of the build mainly comes from the compiler, the performance loss of Lua itself is completely negligible, and xmake rewrites all lua native io interfaces with c, and optimizes the time-consuming interfaces with c . Therefore, through comparative tests, whether it is using Lua or Luajit, the time-consuming to build the project is basically the same, and there is no significant difference. #### Why switch? Because Luajit basically does not support some new architectures, such as: riscv, lonngarch, and the author of luajit has basically not maintained it, some new architecture support and stability repair progress is in a stagnant state. In order to be able to better support more platforms and have obtained faster iterative maintenance, we choose to use Lua will bring many benefits. ### Add Cargo package dependency In this version, we have added support for the Cargo package dependency manager, but it is currently mainly used in Rust projects. Example: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cargo\_deps ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` ### Rust and C++ mixed compilation #### Use cxxbridge to call rust in c++ Example: [cxx\_call\_rust\_library](https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cxx_call_rust_library) ```lua add_rules("mode.debug", "mode.release") add_requires("cargo::cxx 1.0") target("foo") set_kind("static") add_files("src/foo.rs") set_values("rust.cratetype", "staticlib") add_packages("cargo::cxx") target("test") set_kind("binary") add_rules("rust.cxxbridge") add_deps("foo") add_files("src/main.cc") add_files("src/bridge.rsx") ``` foo.rs ```rust #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } pub fn add(a: i32, b: i32) -> i32 { return a + b; } ``` We also need to add the bridge file bridge.rsx to the c++ project ```rust #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } ``` main.cc ```c++ #include #include "bridge.rs.h" int main(int argc, char** argv) { printf("add(1, 2) == %d\n", add(1, 2)); return 0; } ``` #### Calling C++ in Rust Example: [rust\_call\_cxx\_library](https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/rust_call_cxx_library) ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.cc") target("test") set_kind("binary") add_deps("foo") add_files("src/main.rs") ``` main.rs ```rust extern "C" { fn add(a: i32, b: i32) -> i32; } fn main() { unsafe { println!("add(1, 2) = {}", add(1, 2)); } } ``` foo.cc ```c++ extern "C" int add(int a, int b) { return a + b; } ``` ### Added glsl shader compilation rules We have added a new compilation rule of `utils.glsl2spv`, which can introduce glsl shader files such as `*.vert/*.frag` into the project, and then realize automatic compilation to generate `*.spv` files. In addition, we also support binary embedding spv file data in the form of C/C++ header file, which is convenient for program use. #### Compile and generate spv file xmake will automatically call glslangValidator or glslc to compile shaders to generate .spv files, and then output them to the specified `{outputdir = "build"}` directory. ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {outputdir = "build"}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` Note that the `add_packages("glslang")` here is mainly used to import and bind the glslangValidator in the glslang package to ensure that xmake can always use it. Of course, if you have already installed it on your own system, you don’t need to bind this package additionally, but I still recommend adding it. #### Compile and generate c/c++ header files We can also use the bin2c module internally to generate the corresponding binary header file from the compiled spv file, which is convenient for direct import in user code. We only need to enable `{bin2c = true}`. :w ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` Then we can introduce in the code like this: ```c static unsigned char g_test_vert_spv_data[] = { #include "test.vert.spv.h" }; static unsigned char g_test_frag_spv_data[] = { #include "test.frag.spv.h" }; ``` Similar to the usage of bin2c rules, see the complete example: [glsl2spv example](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/glsl2spv) ### Improve C++ Modules construction In the last version, we refactored the C++20 Modules build support, and in this version, we continue to improve it. For the msvc compiler, we have been able to import the std standard library module in the module. In addition, we have fixed the problem that the module import compilation fails when there are dependencies between multiple targets. ### Improve MDK program build configuration In the last version, we added support for building MDK programs. It should be noted that when some mdk programs currently use the microlib library to run, it requires the compiler to add the `__MICROLIB` macro definition, and the linker to add `-- library_type=microlib` and other configurations. In this version, we can directly set to the microlib runtime library through `set_runtimes("microlib")`, and all relevant options can be set automatically. #### Console Program ```lua target("hello") add_deps("foo") add_rules("mdk.console") add_files("src/*.c", "src/*.s") add_includedirs("src/lib/cmsis") set_runtimes("microlib") ``` #### Static library program ```lua add_rules("mode.debug", "mode.release") target("foo") add_rules("mdk.static") add_files("src/foo/*.c") set_runtimes("microlib") ``` ### Improve OpenMP project configuration We have also improved the configuration of the openmp project, which is more simplified and unified. We no longer need to configure additional rules. The same effect can be achieved only through a common openmp package. ```lua add_requires("openmp") target("loop") set_kind("binary") add_files("src/*.cpp") add_packages("openmp") ``` In the previous version, we need to configure this way, and by comparison, we can see that the new configuration is more concise. ```lua add_requires("libomp", {optional = true}) target("loop") set_kind("binary") add_files("src/*.cpp") add_rules("c++.openmp") add_packages("libomp") ``` ## Changelog ### New features * [#1799](https://github.com/xmake-io/xmake/issues/1799): Support mixed rust & c++ target and cargo dependences * Add `utils.glsl2spv` rules to compile *.vert/*.frag shader files to spirv file and binary c header file ### Changes * Switch to Lua5.4 runtime by default * [#1776](https://github.com/xmake-io/xmake/issues/1776): Improve system::find\_package, support to find package from envs * [#1786](https://github.com/xmake-io/xmake/issues/1786): Improve apt:find\_package, support to find alias package * [#1819](https://github.com/xmake-io/xmake/issues/1819): Add precompiled header to cmake generator * Improve C++20 module to support std libraries for msvc * [#1792](https://github.com/xmake-io/xmake/issues/1792): Add custom command in vs project generator * [#1835](https://github.com/xmake-io/xmake/issues/1835): Improve MDK program supports and add `set_runtimes("microlib")` * [#1858](https://github.com/xmake-io/xmake/issues/1858): Improve to build c++20 modules with libraries * Add $XMAKE\_BINARY\_REPO and $XMAKE\_MAIN\_REPO repositories envs * [#1865](https://github.com/xmake-io/xmake/issues/1865): Improve openmp projects * [#1845](https://github.com/xmake-io/xmake/issues/1845): Install pdb files for static library ### Bugs Fixed * Fix semver to parse build string with zero prefix * [#50](https://github.com/libbpf/libbpf-bootstrap/issues/50): Fix rule and build bpf program errors * [#1610](https://github.com/xmake-io/xmake/issues/1610): Fix `xmake f --menu` not responding in vscode and support ConPTY terminal virtkeys --- --- url: /zh/posts/xmake-update-v2.6.1.md --- ### 默认切换到 Lua5.4 运行时 历经几个版本的迭代测试,我们在 2.6.1 版本,正式切换到 Lua5.4 运行时。 不过,这对于用户来说是完全无感知的,基本上没有任何兼容性问题,因为 xmake 对大部分接口都是封装过的,完全消除了 Lua 版本间的兼容性问题。 对于构建性能方面,由于构建的性能瓶颈主要来自编译器,Lua 自身的性能损耗完全可以忽略,而且 xmake 用 c 重写了 lua 原生的所有 io 接口,并且对耗时的接口都用 c 实现了优化。 因此,通过对比测试,不管是使用 Lua 还是 Luajit,构建项目的耗时基本一致,没有明显差异。 #### 为什么要切换? 因为 Luajit 对一些新架构基本不支持,例如:riscv, lonngarch,而且 luajit 作者基本已经不怎么维护它了,一些新架构支持和稳定性修复进展属于停滞状态。 为了能够更好的支持更多的平台,已经获取更快的迭代维护,我们选择使用 Lua 会带来非常多的好处。 ### 添加 Cargo 包依赖 我们在这个版本中,新增了 Cargo 包依赖管理器的支持,不过目前主要用于 Rust 项目。 例子: https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cargo\_deps ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` ### Rust 和 C++ 混合编译 #### 使用 cxxbridge 在 c++ 中调用 rust 例子: [cxx\_call\_rust\_library](https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cxx_call_rust_library) ```lua add_rules("mode.debug", "mode.release") add_requires("cargo::cxx 1.0") target("foo") set_kind("static") add_files("src/foo.rs") set_values("rust.cratetype", "staticlib") add_packages("cargo::cxx") target("test") set_kind("binary") add_rules("rust.cxxbridge") add_deps("foo") add_files("src/main.cc") add_files("src/bridge.rsx") ``` foo.rs ```rust #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } pub fn add(a: i32, b: i32) -> i32 { return a + b; } ``` 我们还需要在 c++ 项目中添加桥接文件 bridge.rsx ```rust #[cxx::bridge] mod foo { extern "Rust" { fn add(a: i32, b: i32) -> i32; } } ``` main.cc ```c++ #include #include "bridge.rs.h" int main(int argc, char** argv) { printf("add(1, 2) == %d\n", add(1, 2)); return 0; } ``` #### 在 Rust 中调用 C++ 例子: [rust\_call\_cxx\_library](https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/rust_call_cxx_library) ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.cc") target("test") set_kind("binary") add_deps("foo") add_files("src/main.rs") ``` main.rs ```rust extern "C" { fn add(a: i32, b: i32) -> i32; } fn main() { unsafe { println!("add(1, 2) = {}", add(1, 2)); } } ``` foo.cc ```c++ extern "C" int add(int a, int b) { return a + b; } ``` ### 新增 glsl shader 编译规则 我们新增了一个 `utils.glsl2spv` 编译规则,可以在项目中引入 `*.vert/*.frag` 等 glsl shader 文件,然后实现自动编译生成 `*.spv` 文件。 另外,我们还支持以 C/C++ 头文件的方式,二进制内嵌 spv 文件数据,方便程序使用。 #### 编译生成 spv 文件 xmake 会自动调用 glslangValidator 或者 glslc 去编译 shaders 生成 .spv 文件,然后输出到指定的 `{outputdir = "build"}` 目录下。 ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {outputdir = "build"}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` 注,这里的 `add_packages("glslang")` 主要用于引入和绑定 glslang 包中的 glslangValidator,确保 xmake 总归能够使用它。 当然,如果用户自己系统上已经安装了它,也可以不用额外绑定这个包,不过我还是建议添加一下。 #### 编译生成 c/c++ 头文件 我们也可以内部借助 bin2c 模块,将编译后的 spv 文件生成对应的二进制头文件,方便用户代码中直接引入,我们只需要启用 `{bin2c = true}`。:w ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` 然后我们可以在代码这么引入: ```c static unsigned char g_test_vert_spv_data[] = { #include "test.vert.spv.h" }; static unsigned char g_test_frag_spv_data[] = { #include "test.frag.spv.h" }; ``` 跟 bin2c 规则的使用方式类似,完整例子见:[glsl2spv example](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/glsl2spv) ### 改进 C++ Modules 构建 上个版本,我们重构了 C++20 Modules 构建支持,而在这个版本中,我们继续对它做了改进。 对于 msvc 编译器,我们已经能够在模块中导入 std 标准库模块,另外,我们修复了多个 target 之间存在依赖时,模块导入编译失败的问题。 ### 改进 MDK 程序构建配置 上个版本,我们新增了 MDK 程序的构建支持,需要注意的是,目前一些 mdk 程序都使用了 microlib 库运行时,它需要编译器加上 `__MICROLIB` 宏定义,链接器加上 `--library_type=microlib` 等各种配置。 而在这个版本中,我们可以通过 `set_runtimes("microlib")` 直接设置到 microlib 运行时库,可以自动设置上所有相关选项。 #### 控制台程序 ```lua target("hello") add_deps("foo") add_rules("mdk.console") add_files("src/*.c", "src/*.s") add_includedirs("src/lib/cmsis") set_runtimes("microlib") ``` #### 静态库程序 ```lua add_rules("mode.debug", "mode.release") target("foo") add_rules("mdk.static") add_files("src/foo/*.c") set_runtimes("microlib") ``` ### 改进 OpenMP 项目配置 我们也改进了 openmp 项目的配置,更加简化和统一,我们不再需要额外配置 rules,仅仅通过一个通用的 openmp 包就可以实现相同的效果。 ```lua add_requires("openmp") target("loop") set_kind("binary") add_files("src/*.cpp") add_packages("openmp") ``` 在之前的版本,我们需要这么配置,对比一下,就能看出新的配置更加的简洁。 ```lua add_requires("libomp", {optional = true}) target("loop") set_kind("binary") add_files("src/*.cpp") add_rules("c++.openmp") add_packages("libomp") ``` ## 更新内容 ### 新特性 * [#1799](https://github.com/xmake-io/xmake/issues/1799): 支持混合 Rust 和 C++ 程序,以及集成 Cargo 依赖库 * 添加 `utils.glsl2spv` 规则去编译 *.vert/*.frag shader 文件生成 spirv 文件和二进制 C 头文件 ### 改进 * 默认切换到 Lua5.4 运行时 * [#1776](https://github.com/xmake-io/xmake/issues/1776): 改进 system::find\_package,支持从环境变量中查找系统库 * [#1786](https://github.com/xmake-io/xmake/issues/1786): 改进 apt:find\_package,支持查找 alias 包 * [#1819](https://github.com/xmake-io/xmake/issues/1819): 添加预编译头到 cmake 生成器 * 改进 C++20 Modules 为 msvc 支持 std 标准库 * [#1792](https://github.com/xmake-io/xmake/issues/1792): 添加自定义命令到 vs 工程生成器 * [#1835](https://github.com/xmake-io/xmake/issues/1835): 改进 MDK 程序构建支持,增加 `set_runtimes("microlib")` * [#1858](https://github.com/xmake-io/xmake/issues/1858): 改进构建 c++20 modules,修复跨 target 构建问题 * 添加 $XMAKE\_BINARY\_REPO 和 $XMAKE\_MAIN\_REPO 仓库设置环境变量 * [#1865](https://github.com/xmake-io/xmake/issues/1865): 改进 openmp 工程 * [#1845](https://github.com/xmake-io/xmake/issues/1845): 为静态库安装 pdb 文件 ### Bugs 修复 * 修复语义版本中解析带有 0 前缀的 build 字符串问题 * [#50](https://github.com/libbpf/libbpf-bootstrap/issues/50): 修复 rule 和构建 bpf 程序 bug * [#1610](https://github.com/xmake-io/xmake/issues/1610): 修复 `xmake f --menu` 在 vscode 终端下按键无响应,并且支持 ConPTY 终端虚拟按键 --- --- url: /posts/xmake-update-v2.6.2.md --- ### Build Linux kernel driver module Xmake may be the first third-party build tool that provides built-in support for Linux kernel driver development. Although there are also instructions on how CMake configures and builds Linux drivers on the Internet, most of them use `add_custom_command` to customize various commands, and then execute `echo` to splice and generate Linux Makefile files by themselves. In other words, it is actually a build that relies on the Makefile of the Linux kernel source code to execute, so if you want to add some compilation configuration and macro definitions yourself, it will be very troublesome. With Xmake, we can provide more flexible configurability, simpler configuration files, and features such as one-click compilation, automatic dependency pull integration, automatic download and integration of Linux kernel source code, and cross-compilation of kernel drivers. #### Hello World We intuitively feel it through one of the simplest character drivers. First, we prepare a Hello World driver code, for example: ```lua add_requires("linux-headers", {configs = {driver_modules = true}}) target("hello") add_rules("platform.linux.driver") add_files("src/*.c") add_packages("linux-headers") set_license("GPL-2.0") ``` Its configuration is very simple. You only need to configure the linux-headers package that supports the module, and then apply the `platform.linux.driver` build rule. Then directly execute the xmake command, compile with one key, and generate the kernel driver module hello.ko. ```console $ xmake [20%]: ccache compiling.release src/add.c [20%]: ccache compiling.release src/hello.c [60%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! ``` Is it simple? Maybe you would say that it is not much different from directly configuring with Makefile and then compiling with make. So the point is here. Xmake will automatically pull the kernel source code for the specified version for you. Make will not, and CMake will not. Users must install them by themselves through `sudo apt install linux-headers-generic`. But Xmake doesn't need it. In the one-click compilation above, I actually omitted part of the output, which is actually the case. ```console $ xmake note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> m4 1.4.19 [from:linux-headers,bison,flex,elfutils] -> flex 2.6.4 [from:bc,linux-headers] -> bison 3.8.2 [from:bc,linux-headers] -> ed 1.17 [from:bc,linux-headers] -> texinfo 6.7 [from:bc,linux-headers] -> bc 1.07.1 [from:linux-headers] -> pkg-config 0.29.2 [from:linux-headers] -> openssl 1.1.1l [private, from:linux-headers] -> elfutils 0.183 [private, from:linux-headers] -> linux-headers 5.10.46 [driver_modules:y] please input: y (y/n/m) => download https://github.com/xmake-mirror/ed/archive/refs/tags/1.17.tar.gz .. ok => install ed 1.17 .. ok => download https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.xz .. ok => download https://ftp.gnu.org/gnu/texinfo/texinfo-6.7.tar.xz .. ok => download https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz .. ok => download https://github.com/openssl/openssl/archive/OpenSSL_1_1_1l.zip .. ok => install m4 1.4.19 .. ok => download https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz .. ok => install texinfo 6.7 .. ok => install pkg-config 0.29.2 .. ok => download http://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz .. ok => install flex 2.6.4 .. ok => download https://sourceware.org/elfutils/ftp/0.183/elfutils-0.183.tar.bz2 .. ok => install elfutils 0.183 .. ok => install bison 3.8.2 .. ok => download https://ftp.gnu.org/gnu/bc/bc-1.07.1.tar.gz .. ok => install bc 1.07.1 .. ok => install openssl 1.1.1l .. ok => download https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.46.tar.xz .. ok => install linux-headers 5.10.46 .. ok [16%]: ccache compiling.release src/add.c [16%]: ccache compiling.release src/hello.c [16%]: ccache compiling.release src/hello.mod.c [66%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! ``` When compiling for the first time, Xmake will pull all dependencies. If the user has installed them through third-party package management such as apt, Xmake will give priority to the version already installed on the system, saving the download and installation process. In other words, no matter what environment you are in, users don't need to care about how to build a kernel-driven development environment. They only need one `xmake` command to get everything done. And these dependency pulls are implemented through the `add_requires("linux-headers", {configs = {driver_modules = true}})` configuration package. In addition, we can also see the complete build command parameters. ```console $ xmake -v [20%]: ccache compiling.release src/add.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr /src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux -headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr /src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\ "hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno -80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect -branch-re gister -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops= 1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone- fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"add\ "-o build/.objs/hello/linux/x86_64/release/src/add.c.o src/add.c [20%]: ccache compiling.release src/hello.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr /src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux -headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr /src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\ "hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno -80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect -branch-re gister -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops= 1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone- fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"hello\ "-o build/.objs/hello/linux/x86_64/release/src/hello.co src/hello.c [60%]: linking.release build/linux/x86_64/release/hello.ko /usr/bin/ld -m elf_x86_64 -r -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/ release/src/add.co build/.objs/hello/linux/x86_64/release/src/hello.co /usr/src/linux-headers-5.11.0-41-generic/scripts/mod/modpost -m -a -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/Module .symvers -e -N -T- WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr /src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux -headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr /src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\ "hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno -80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect -branch-re gister -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops= 1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone- fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -o build/.objs/ hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod. c /usr/bin/ld -m elf_x86_64 -r --build-id=sha1 -T /usr/src/linux-headers-5.11.0-41-generic/scripts/module.lds -o build/linux/x86_64/ release/hello.ko build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/ release/hello.ko.mod.o ``` #### Use a specific version of the kernel source code We can also specify version semantic rules and select the kernel source code we need as the build source. ```lua add_requires("linux-headers 5.9.x", {configs = {driver_modules = true}}) ``` #### Cross compilation We also support cross-compilation of kernel driver modules, such as using cross-compilation tool chain on Linux x86\_64 to build Linux Arm/Arm64 driver modules. We only need to prepare our own cross-compilation tool chain, specify its root directory through `--sdk=`, then switch to the `-p cross` platform configuration, and finally specify the architecture arm/arm64 to be built. Similarly, we don't need to care about how to prepare linux-headers to support cross-compilation. Xmake's dependency package management will help you prepare everything and pull and build the kernel source code that supports the corresponding architecture. The cross toolchain used here can be downloaded from here: [Download toolchains](https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/) For more, cross-compilation configuration documents, see: [Configure cross-compilation](https://xmake.io/#/guide/configuration#common-cross-compilation-configuration) Note: Currently only arm/arm64 cross-compilation architecture is supported, and more platform architectures will be supported in the future. ##### Build Arm driver module ```console $ xmake f -p cross -a arm --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf -c $ xmake [20%]: ccache compiling.release src/add.c [20%]: ccache compiling.release src/hello.c [60%]: linking.release build/cross/arm/release/hello.ko [100%]: build ok! ``` ##### Build Arm64 driver module ```console $ xmake f -p cross -a arm64 --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu -c $ xmake [20%]: ccache compiling.release src/add.c [20%]: ccache compiling.release src/hello.c [60%]: linking.release build/cross/arm64/release/hello.ko [100%]: build ok! ``` ### Group batch build and run support In the early days, we have supported the setting of target grouping through `set_group` to realize the management and display of the source file grouping of the vs/vsxmake project under VS. However, this grouping is limited to this feature and is not used in other places. In the new version, we continue to improve and use the grouping feature to achieve designated construction of a batch of target programs and batch operation of a batch of target programs. What is this usually useful for, for example, it can be used to provide functions such as `Run all tests` and `Run all benchmarks`. #### Compile and specify a batch of target programs We can use `set_group()` to mark a given target as `test/benchmark/...` and use `set_default(false)` to disable to build it by default. In this way, Xmake will not build them by default, but we can specify to build a batch of target programs through the `xmake -g xxx` command. For example, we can use this feature to build all tests. ```lua target("test1") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") target("test2") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") ``` ```console $ xmake -g test $ xmake --group=test ``` #### Run a specified batch of target programs We can also specify to run all test programs with the `test` group by setting the group. This is usually very useful. Before that, if we want to implement the `Run all tests` function, we can only call `os.exec` by defining a `task("tests")` to execute the test targets one by one. The configuration is cumbersome. The requirements for users are relatively high. And now, we only need to mark the test target programs that need to be executed as `set_group("test")`, and then run them in batches. ```console $ xmake run -g test $ xmake run --group=test ``` #### Support group pattern matching In addition, we can also support grouped pattern matching, which is very flexible: ``` $ xmake build -g test_* $ xmake run -g test/foo_* $ xmake build -g bench* $ xmake run -g bench* ``` For more information: [#1913](https://github.com/xmake-io/xmake/issues/1913) ### Improve the search and integration of CMake package sources In the previous version, we provided `find_package("cmake::xxx")` to find packages inside cmake, but this method is still very cumbersome for users to integrate and use. Therefore, in the new version, we further improve it, through `add_requires` to achieve a unified and fast cmake package integration. ```lua add_requires("cmake::ZLIB", {alias = "zlib", system = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` We specify `system = true` to tell xmake to force cmake to find the package from the system. If it cannot be found, the installation logic will not be followed, because cmake does not provide the installation function of package managers such as vcpkg/conan. Only the package search feature is provided. #### Specify version ```lua add_requires("cmake::OpenCV 4.1.1", {system = true}) ``` #### Specified components ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}})) ``` #### Default switch ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}}) ``` It is equivalent to predefine some configurations in CMakeLists.txt before calling find\_package internally to find the package to control the find\_package search strategy and status. ``` set(Boost_USE_STATIC_LIB ON) - will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system) ``` #### Set environment variables ```lua add_requires("cmake::OpenCV", {system = true, configs = {envs = {CMAKE_PREFIX_PATH = "xxx"}}}) ``` #### Specify custom FindFoo.cmake module script directory mydir/cmake\_modules/FindFoo.cmake ```lua add_requires("cmake::Foo", {system = true, configs = {moduledirs = "mydir/cmake_modules"}}) ``` Related issues: [#1632](https://github.com/xmake-io/xmake/issues/1632) ### xmake-idea plugin update [xmake-idea](https://github.com/xmake-io/xmake-idea) Due to personal time and energy, this plug-in has not spent time to maintain and update: and IDEA plug-ins have a lot of compatibility issues , As long as it is not used for a period of time, it cannot be used normally on the new Idea/Clion. Recently, I took some time to fix some compatibility issues, such as the problem of freezing when creating a project on Windows, and problems such as the inability to install the new version of Clion. At present, the latest version should be able to be used normally on all platforms. ## Some other things worth mentioning ### Year-end summary This is the last version I released in 2021. After a year, Xmake has gradually grown into a more powerful build tool. By the end of this year, Xmake had a total of 4.2k stars, processed 1.9k issues/pr, and had more than 8k multiple commits. The official package management repository [xmake-repo](https://github.com/xmake-io/xmake-repo) has also included nearly 500+ commonly used dependency packages. ### Thanks Thank you contributors for your contributions to the xmake-repo repository and Xmake. For the complete list of contributors, see: [Contributors](https://github.com/xmake-io/xmake/graphs/contributors). Thank you very much for your support for Xmake's sponsorship, so that I can have enough motivation to maintain it. For the complete list of donations, please see: [Sponsors](https://xmake.io/#/about/sponsor). ## Changelog ### New features * [#1902](https://github.com/xmake-io/xmake/issues/1902): Support to build linux kernel driver modules * [#1913](https://github.com/xmake-io/xmake/issues/1913): Build and run targets with given group pattern ### Change * [#1872](https://github.com/xmake-io/xmake/issues/1872): Escape characters for set\_configvar * [#1888](https://github.com/xmake-io/xmake/issues/1888): Improve windows installer to avoid remove other files * [#1895](https://github.com/xmake-io/xmake/issues/1895): Improve `plugin.vsxmake.autoupdate` rule * [#1893](https://github.com/xmake-io/xmake/issues/1893): Improve to detect icc and ifort toolchains * [#1905](https://github.com/xmake-io/xmake/pull/1905): Add support of external headers without experimental for msvc * [#1904](https://github.com/xmake-io/xmake/pull/1904): Improve vs201x generator * Add `XMAKE_THEME` envirnoment variable to switch theme * [#1907](https://github.com/xmake-io/xmake/issues/1907): Add `-f/--force` to force to create project in a non-empty directory * [#1917](https://github.com/xmake-io/xmake/pull/1917): Improve to find\_package and configurations ### Bugs fixed * [#1885](https://github.com/xmake-io/xmake/issues/1885): Fix package:fetch\_linkdeps * [#1903](https://github.com/xmake-io/xmake/issues/1903): Fix package link order --- --- url: /zh/posts/xmake-update-v2.6.2.md --- ## 新版本改动 这个版本主要新增两大特性: 1. Linux 内核驱动模块的构建支持 2. 分组构建和批量运行支持,可用于实现 `Run all tests` 功能 剩下的主要是一些零散的功能改进和 Bugs 修复,可以看下文末的更新内容明细,一些比较大的改动,下面也会逐一说明。 ## 新特性介绍 ### 构建 Linux 内核驱动模块 Xmake 也许是首个提供 Linux 内核驱动开发 内置支持的第三方构建工具了。 尽管网上也有介绍 CMake 如何去配置构建 linux 驱动,但是大都是通过 `add_custom_command` 方式自定义各种命令,然后执行 `echo` 去自己拼接生成 Linux 的 Makefile 文件。 也就是说,其实还是依赖 Linux 内核源码的 Makefile 来执行的构建,因此如果想自己追加一些编译配置和宏定义都会非常麻烦。 而使用 Xmake,我们可以提供更加灵活的可配置性,更加简单的配置文件,以及一键编译、自动依赖拉取集成、Linux kernel 源码自动下载集成,内核驱动交叉编译等特性。 #### Hello World 我们通过一个最简单的字符驱动来直观感受下。 首先,我们准备一个 Hello World 驱动代码,例如: ```lua add_requires("linux-headers", {configs = {driver_modules = true}}) target("hello") add_rules("platform.linux.driver") add_files("src/*.c") add_packages("linux-headers") set_license("GPL-2.0") ``` 它的配置非常简单,只需要配置上支持模块的 linux-headers 包,然后应用 `platform.linux.driver` 构建规则就行了。 然后直接执行 xmake 命令,一键编译,生成内核驱动模块 hello.ko。 ```console $ xmake [ 20%]: ccache compiling.release src/add.c [ 20%]: ccache compiling.release src/hello.c [ 60%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! ``` 简单么,也许你会说,这跟直接用 Makefile 配置,然后 make 编译也没啥太大区别么。 那么重点来了,Xmake 是会自动帮你拉取指定版本依赖内核源码,make 不会,CMake 也不会,用户必须通过 `sudo apt install linux-headers-generic` 自己安装它们。 但是 Xmake 不需要,上面的一键编译,我其实省略了部分输出,实际上是这样的。 ```console $ xmake note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> m4 1.4.19 [from:linux-headers,bison,flex,elfutils] -> flex 2.6.4 [from:bc,linux-headers] -> bison 3.8.2 [from:bc,linux-headers] -> ed 1.17 [from:bc,linux-headers] -> texinfo 6.7 [from:bc,linux-headers] -> bc 1.07.1 [from:linux-headers] -> pkg-config 0.29.2 [from:linux-headers] -> openssl 1.1.1l [private, from:linux-headers] -> elfutils 0.183 [private, from:linux-headers] -> linux-headers 5.10.46 [driver_modules:y] please input: y (y/n/m) => download https://github.com/xmake-mirror/ed/archive/refs/tags/1.17.tar.gz .. ok => install ed 1.17 .. ok => download https://ftp.gnu.org/gnu/m4/m4-1.4.19.tar.xz .. ok => download https://ftp.gnu.org/gnu/texinfo/texinfo-6.7.tar.xz .. ok => download https://pkgconfig.freedesktop.org/releases/pkg-config-0.29.2.tar.gz .. ok => download https://github.com/openssl/openssl/archive/OpenSSL_1_1_1l.zip .. ok => install m4 1.4.19 .. ok => download https://github.com/westes/flex/releases/download/v2.6.4/flex-2.6.4.tar.gz .. ok => install texinfo 6.7 .. ok => install pkg-config 0.29.2 .. ok => download http://ftp.gnu.org/gnu/bison/bison-3.8.2.tar.gz .. ok => install flex 2.6.4 .. ok => download https://sourceware.org/elfutils/ftp/0.183/elfutils-0.183.tar.bz2 .. ok => install elfutils 0.183 .. ok => install bison 3.8.2 .. ok => download https://ftp.gnu.org/gnu/bc/bc-1.07.1.tar.gz .. ok => install bc 1.07.1 .. ok => install openssl 1.1.1l .. ok => download https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.46.tar.xz .. ok => install linux-headers 5.10.46 .. ok [ 16%]: ccache compiling.release src/add.c [ 16%]: ccache compiling.release src/hello.c [ 16%]: ccache compiling.release src/hello.mod.c [ 66%]: linking.release build/linux/x86_64/release/hello.ko [100%]: build ok! ``` 首次编译,Xmake 会拉取所有依赖,如果用户自己已经通过 apt 等第三方包管理安装了它们,Xmake 也会优先用系统已经安装的版本,省去下载安装过程。 也就是说,不管在哪个环境,用户都不需要关心如何去搭建内核驱动开发环境,仅仅只需要一个 `xmake` 命令,就能搞定一切。 而这些依赖拉取,都是通过 `add_requires("linux-headers", {configs = {driver_modules = true}})` 配置包来实现的。 另外,我们也可以看完整构建命令参数。 ```console $ xmake -v [ 20%]: ccache compiling.release src/add.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"add\" -o build/.objs/hello/linux/x86_64/release/src/add.c.o src/add.c [ 20%]: ccache compiling.release src/hello.c /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -DKBUILD_BASENAME=\"hello\" -o build/.objs/hello/linux/x86_64/release/src/hello.c.o src/hello.c [ 60%]: linking.release build/linux/x86_64/release/hello.ko /usr/bin/ld -m elf_x86_64 -r -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/src/add.c.o build/.objs/hello/linux/x86_64/release/src/hello.c.o /usr/src/linux-headers-5.11.0-41-generic/scripts/mod/modpost -m -a -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/Module.symvers -e -N -T - WARNING: modpost: Symbol info of vmlinux is missing. Unresolved symbol check will be entirely skipped. /usr/bin/ccache /usr/bin/gcc -c -m64 -O2 -std=gnu89 -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated -I/usr/src/linux-headers-5.11.0-41-generic/include -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/arch/x86/include/generated/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/uapi -I/usr/src/linux-headers-5.11.0-41-generic/include/generated/uapi -D__KERNEL__ -DMODULE -DKBUILD_MODNAME=\"hello\" -DCONFIG_X86_X32_ABI -isystem /usr/lib/gcc/x86_64-linux-gnu/10/include -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/kconfig.h -include /usr/src/linux-headers-5.11.0-41-generic/include/linux/compiler_types.h -nostdinc -mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -mno-80387 -mno-fp-ret-in-387 -mpreferred-stack-boundary=3 -mskip-rax-setup -mtune=generic -mno-red-zone -mcmodel=kernel -mindirect-branch=thunk-extern -mindirect-branch-register -mrecord-mcount -fmacro-prefix-map=./= -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -fcf-protection=none -falign-jumps=1 -falign-loops=1 -fno-asynchronous-unwind-tables -fno-jump-tables -fno-delete-null-pointer-checks -fno-allow-store-data-races -fno-reorder-blocks -fno-ipa-cp-clone -fno-partial-inlining -fstack-protector-strong -fno-inline-functions-called-once -falign-functions=32 -fno-strict-overflow -fno-stack-check -fconserve-stack -o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.c /usr/bin/ld -m elf_x86_64 -r --build-id=sha1 -T /usr/src/linux-headers-5.11.0-41-generic/scripts/module.lds -o build/linux/x86_64/release/hello.ko build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.o build/.objs/hello/linux/x86_64/release/build/linux/x86_64/release/hello.ko.mod.o ``` #### 使用特定版本的内核源码 我们也可以指定版本语义规则,选取自己需要的内核源码作为构建源。 ```lua add_requires("linux-headers 5.9.x", {configs = {driver_modules = true}}) ``` #### 交叉编译 我们也支持内核驱动模块的交叉编译,比如在 Linux x86\_64 上使用交叉编译工具链来构建 Linux Arm/Arm64 的驱动模块。 我们只需要准备好自己的交叉编译工具链,通过 `--sdk=` 指定它的根目录,然后配置切换到 `-p cross` 平台, 最后指定需要构建的架构 arm/arm64 即可。 同样的,我们不用关心如何准备 linux-headers 去支持交叉编译,Xmake 的依赖包管理会帮你准本好一切,拉取构建支持对应架构的内核源码。 这里用到的交叉工具链,可以从这里下载: [Download toolchains](https://releases.linaro.org/components/toolchain/binaries/latest-7/aarch64-linux-gnu/) 更多,交叉编译配置文档,见:[配置交叉编译](https://xmake.io/zh/) 注:目前仅仅支持 arm/arm64 交叉编译架构,后续会支持更多的平台架构。 ##### 构建 Arm 驱动模块 ```console $ xmake f -p cross -a arm --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_arm-linux-gnueabihf -c $ xmake [ 20%]: ccache compiling.release src/add.c [ 20%]: ccache compiling.release src/hello.c [ 60%]: linking.release build/cross/arm/release/hello.ko [100%]: build ok! ``` ##### 构建 Arm64 驱动模块 ```console $ xmake f -p cross -a arm64 --sdk=/mnt/gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnu -c $ xmake [ 20%]: ccache compiling.release src/add.c [ 20%]: ccache compiling.release src/hello.c [ 60%]: linking.release build/cross/arm64/release/hello.ko [100%]: build ok! ``` ### 分组批量构建和运行支持 早期,我们已经支持了通过 `set_group` 设置目标分组,实现 vs/vsxmake 工程在 vs 下的源文件分组管理展示。 但是,这个分组仅限于这个特性,没有用于其他地方,而新版本中,我们继续改进利用分组特性,实现指定构建一批目标程序,以及批量运行一批目标程序。 这通常有什么用呢,比如可以用来提供 `Run all tests` 和 `Run all benchmarks` 等功能。 #### 编译指定一批目标程序 我们可以使用 `set_group()` 将给定的目标标记为 `test/benchmark/...` 并使用 `set_default(false)` 禁用来默认构建它。 这样,默认情况下 Xmake 不会去构建它们,但是我们可以通过 `xmake -g xxx` 命令就能指定构建一批目标程序了。 比如,我们可以使用此功能来构建所有测试。 ```lua target("test1") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") target("test2") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") ``` ```console $ xmake -g test $ xmake --group=test ``` #### 运行指定一批目标程序 我们也可以通过设置分组,来指定运行所有带有 `test` 分组的测试程序。 这通常非常有用,在此之前想要实现 `Run all tests` 功能,我们只能通过定义一个 `task("tests")` 在里面调用 `os.exec` 去挨个执行测试目标,配置比较繁琐,对用户要求比较高。 而现在,我们只需要对需要执行的测试目标程序标记为 `set_group("test")`,然后就可以批量运行它们了。 ```console $ xmake run -g test $ xmake run --group=test ``` #### 支持分组模式匹配 另外,我们还可以支持分组的模式匹配,非常的灵活: ``` $ xmake build -g test_* $ xmake run -g test/foo_* $ xmake build -g bench* $ xmake run -g bench* ``` 更多信息见:[#1913](https://github.com/xmake-io/xmake/issues/1913) ### 改进 CMake 包源的查找和集成 之前的版本中,我们提供了 `find_package("cmake::xxx")` 来查找 cmake 内部的包,但是这种方式对于用户集成使用还是很繁琐。 因此,新版本中,我们进一步改进它,通过 `add_requires` 来实现统一快速的 cmake 包集成。 ```lua add_requires("cmake::ZLIB", {alias = "zlib", system = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` 我们指定 `system = true` 告诉 xmake 强制从系统中调用 cmake 查找包,如果找不到,不再走安装逻辑,因为 cmake 没有提供类似 vcpkg/conan 等包管理器的安装功能, 只提供了包查找特性。 #### 指定版本 ```lua add_requires("cmake::OpenCV 4.1.1", {system = true}) ``` #### 指定组件 ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}})} ``` #### 预设开关 ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}}) ``` 相当于内部调用 find\_package 查找包之前,在 CMakeLists.txt 中预定义一些配置,控制 find\_package 的查找策略和状态。 ``` set(Boost_USE_STATIC_LIB ON) -- will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system) ``` #### 设置环境变量 ```lua add_requires("cmake::OpenCV", {system = true, configs = {envs = {CMAKE_PREFIX_PATH = "xxx"}}}) ``` #### 指定自定义 FindFoo.cmake 模块脚本目录 mydir/cmake\_modules/FindFoo.cmake ```lua add_requires("cmake::Foo", {system = true, configs = {moduledirs = "mydir/cmake_modules"}}) ``` 相关 issues: [#1632](https://github.com/xmake-io/xmake/issues/1632) ### xmake-idea 插件更新 [xmake-idea](https://github.com/xmake-io/xmake-idea) 这个插件由于个人时间和精力的关系,一直没有花时间去维护更新,而 IDEA 插件的兼容性问题有非常多,只要一段时间不用,就无法在新的 Idea/Clion 上正常使用。 最近,我花了点时间,修复了一些兼容性问题,比如 Windows 上创建工程会卡死的问题,新版本 Clion 无法安装等问题。 目前,最新版本应该可以在全平台正常使用了。 ## 另外一些值得提起的事情 ### 年终总结 这是 2021 年我发布的最后一个版本,这一年下来,经历了很多,Xmake 也在逐渐成长为一个更加强大的构建工具。 到今年年底,Xmake 总共收获了 4.2k stars,处理了 1.9k 的 issues/pr,超过 8k 多次 commits。 而官方的包管理仓库 [xmake-repo](https://github.com/xmake-io/xmake-repo) 也已经收录了近 500+ 的常用依赖包。 ### 感谢 感谢各位贡献者对 xmake-repo 仓库 和 Xmake 的贡献,完整贡献者列表见:[Contributors](https://github.com/xmake-io/xmake/graphs/contributors)。 也非常感谢大家对 Xmake 的赞助的支持,使得我能够有足够的动力去持续维护,完整捐助列表见:[Sponsors](https://xmake.io/zh/)。 ## 更新内容 ### 新特性 * [#1902](https://github.com/xmake-io/xmake/issues/1902): 支持构建 linux 内核驱动模块 * [#1913](https://github.com/xmake-io/xmake/issues/1913): 通过 group 模式匹配,指定构建和运行一批目标程序 ### 改进 * [#1872](https://github.com/xmake-io/xmake/issues/1872): 支持转义 set\_configvar 中字符串值 * [#1888](https://github.com/xmake-io/xmake/issues/1888): 改进 windows 安装器,避免错误删除其他安装目录下的文件 * [#1895](https://github.com/xmake-io/xmake/issues/1895): 改进 `plugin.vsxmake.autoupdate` 规则 * [#1893](https://github.com/xmake-io/xmake/issues/1893): 改进探测 icc 和 ifort 工具链 * [#1905](https://github.com/xmake-io/xmake/pull/1905): 改进 msvc 对 external 头文件搜索探测支持 * [#1904](https://github.com/xmake-io/xmake/pull/1904): 改进 vs201x 工程生成器 * 添加 `XMAKE_THEME` 环境变量去切换主题配置 * [#1907](https://github.com/xmake-io/xmake/issues/1907): 添加 `-f/--force` 参数使得 `xmake create` 可以在费控目录被强制创建 * [#1917](https://github.com/xmake-io/xmake/pull/1917): 改进 find\_package 和配置 ### Bugs 修复 * [#1885](https://github.com/xmake-io/xmake/issues/1885): 修复 package:fetch\_linkdeps 链接顺序问题 * [#1903](https://github.com/xmake-io/xmake/issues/1903): 修复包链接顺序 --- --- url: /posts/xmake-update-v2.6.3.md --- ## New version changes This version mainly adds the following features: 1. Implement version selection of vcpkg package through vcpkg's manifest mode 2. Python module build support 3. Support integration of Xrepo/Xmake package management in CMakeLists.txt The rest are mainly some scattered functional improvements and Bugs fixes. You can see the details of the update at the end of the following. Some major changes will be explained one by one below. ## Introduction of new features ### Support Vcpkg manifest mode In the new version, Xmake adds vcpkg manifest mode support, through which we can support the version selection of vcpkg package, for example: ```lua add_requires("vcpkg::zlib 1.2.11+10") add_requires("vcpkg::fmt >=8.0.1", {configs = {baseline = "50fd3d9957195575849a49fa591e645f1d8e7156"}}) add_requires("vcpkg::libpng", {configs = {features = {"apng"}}}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("vcpkg::zlib", "vcpkg::fmt", "vcpkg::libpng") ``` However, the version selection of vcpkg is still quite limited. It must be hard-coded to specify the baseline, and version semantic selection such as `<=1.0`, `1.x` is not supported, but it is better than the previous version that cannot be selected. ### Using Xrepo's package management in CMake CMake wrapper for [Xrepo](https://xrepo.xmake.io/) C and C++ package manager. This allows using CMake to build your project, while using Xrepo to manage dependent packages. This project is partially inspired by [cmake-conan](https://github.com/conan-io/cmake-conan). Example use cases for this project: * Existing CMake projects which want to use Xrepo to manage packages. * New projects which have to use CMake, but want to use Xrepo to manage packages. #### Use package from official repository Xrepo official repository: [xmake-repo](https://github.com/xmake-io/xmake-repo) [xrepo.cmake](https://github.com/xmake-io/xrepo-cmake/blob/main/xrepo.cmake) provides `xrepo_package` function to manage packages. ```cmake xrepo_package( "foo 1.2.3" [CONFIGS feature1=true,feature2=false] [MODE debug|release] [OUTPUT verbose|diagnosis|quiet] [DIRECTORY_SCOPE] ) ``` Some of the function arguments correspond directly to Xrepo command options. After calling `xrepo_package(foo)`, there are two ways to use `foo` package: * Call `find_package(foo)` if package provides cmake modules to find it * Refer to CMake [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) documentation for more details * If the package does not provide cmake modules, `foo_INCLUDE_DIR` and `foo_LINK_DIR` variables will be set to the package include and library paths. Use these variables to setup include and library paths in your CMake code. * If `DIRECTORY_SCOPE` is specified, `xrepo_package` will run following code (so that user only need to specify lib name in `target_link_libraries`) ```cmake include_directories(foo_INCLUDE_DIR) link_directories(foo_LINK_DIR) ``` Here's an example `CMakeLists.txt` that uses `gflags` package version 2.2.2 managed by Xrepo. ```cmake cmake_minimum_required(VERSION 3.13.0) project(foo) # Download xrepo.cmake if not exists in build directory. if(NOT EXISTS "${CMAKE_BINARY_DIR}/xrepo.cmake") message(STATUS "Downloading xrepo.cmake from https://github.com/xmake-io/xrepo-cmake/") # mirror https://cdn.jsdelivr.net/gh/xmake-io/xrepo-cmake@main/xrepo.cmake file(DOWNLOAD "https://raw.githubusercontent.com/xmake-io/xrepo-cmake/main/xrepo.cmake" "${CMAKE_BINARY_DIR}/xrepo.cmake" TLS_VERIFY ON) endif() # Include xrepo.cmake so we can use xrepo_package function. include(${CMAKE_BINARY_DIR}/xrepo.cmake) # Call `xrepo_package` function to use gflags 2.2.2 with specific configs. xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true") # `xrepo_package` sets `gflags_DIR` variable in parent scope because gflags # provides cmake modules. So we can now call `find_package` to find gflags # package. find_package(gflags CONFIG COMPONENTS shared) ``` #### Use package from 3rd repository In addition to installing packages from officially maintained repository, Xrepo can also install packages from third-party package managers such as vcpkg/conan/conda/pacman/homebrew/apt/dub/cargo. For the use of the command line, we can refer to the documentation: [Xrepo command usage](https://xrepo.xmake.io/#/getting_started#install-packages-from-third-party-package-manager) We can also use it directly in cmake to install packages from third-party repositories, just add the repository name as a namespace. e.g. `vcpkg::zlib`, `conan::pcre2` ##### Conan ```cmake xrepo_package("conan::gflags/2.2.2") ``` ##### Conda ```cmake xrepo_package("conda::gflags 2.2.2") ``` ##### Vcpkg ```cmake xrepo_package("vcpkg::gflags") ``` ##### Homebrew ```cmake xrepo_package("brew::gflags") ``` ### Python module building support We can use this rule to generate python library modules with pybind11, which will adjust the module name of the python library. ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library") add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` with soabi: ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library", {soabi = true}) add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` ### Added delete header file list interface Through this interface, the specified file can be removed from the list of header files added by the `add_headerfiles` interface, for example: ```lua target("test") add_headerfiles("src/*.h") remove_headerfiles("src/test.h") ``` In the above example, all header files except `test.h` can be added from the `src` directory, of course, this can also be achieved by `add_headerfiles("src/*.h|test.h")` to achieve the same purpose , but this way is more flexible. ### Added on\_config configuration script After `xmake config` is executed, this script is executed before Build, which is usually used for configuration work before compilation. It differs from on\_load in that on\_load is executed as soon as the target is loaded, and the execution timing is earlier. If some configuration cannot be configured prematurely in on\_load, it can be configured in on\_config. In addition, its execution time is earlier than before\_build, and the approximate execution flow is as follows: ``` on_load -> after_load -> on_config -> before_build -> on_build -> after_build ``` ### Built-in Github proxy mirror configuration Xmake provides some built-in mirror configurations that can be used directly, such as github's mirror acceleration: ```console $ xmake g --proxy_pac=github_mirror.lua ``` We don't have to write pac.lua ourselves, we can use it directly to speed up the download of github sources. ## Changelog ### New features * [#1298](https://github.com/xmake-io/xmake/issues/1928): Support vcpkg manifest mode and select version for package/install * [#1896](https://github.com/xmake-io/xmake/issues/1896): Add `python.library` rule to build pybind modules * [#1939](https://github.com/xmake-io/xmake/issues/1939): Add `remove_files`, `remove_headerfiles` and mark `del_files` as deprecated * Made on\_config as the official api for rule/target * Add riscv32/64 support * [#1970](https://github.com/xmake-io/xmake/issues/1970): Add CMake wrapper for Xrepo C and C++ package manager. * Add builtin github mirror pac files, `xmake g --proxy_pac=github_mirror.lua` ### Changes * [#1923](https://github.com/xmake-io/xmake/issues/1923): Improve to build linux driver, support set custom linux-headers path * [#1962](https://github.com/xmake-io/xmake/issues/1962): Improve armclang toolchain to support to build asm * [#1959](https://github.com/xmake-io/xmake/pull/1959): Improve vstudio project generator * [#1969](https://github.com/xmake-io/xmake/issues/1969): Add default option description ### Bugs fixed * [#1875](https://github.com/xmake-io/xmake/issues/1875): Fix deploy android qt apk issue * [#1973](https://github.com/xmake-io/xmake/issues/1973): Fix merge static archive --- --- url: /zh/posts/xmake-update-v2.6.3.md --- ## 新版本改动 这个版本主要新增下面几个特性: 1. 通过 vcpkg 的清单模式实现 vcpkg 包的版本选择 2. python 模块构建支持 3. 支持在 CMakeLists.txt 中集成 Xrepo/Xmake 包管理 剩下的主要是一些零散的功能改进和 Bugs 修复,可以看下文末的更新内容明细,一些比较大的改动,下面也会逐一说明。 ## 新特性介绍 ### 支持 Vcpkg 清单模式 新版本中,Xmake 新增了 vcpkg 清单模式支持,通过它,我们就能支持 vcpkg 包的版本选择,例如: ```lua add_requires("vcpkg::zlib 1.2.11+10") add_requires("vcpkg::fmt >=8.0.1", {configs = {baseline = "50fd3d9957195575849a49fa591e645f1d8e7156"}}) add_requires("vcpkg::libpng", {configs = {features = {"apng"}}}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("vcpkg::zlib", "vcpkg::fmt", "vcpkg::libpng") ``` 但是,vcpkg 的版本选择限制还是不少,必须要硬编码指定 baseline,而且还不支持 `<=1.0`, `1.x` 等版本语义选择,不过总比之前不能选择版本好了不少。 ### 在 CMake 中使用 Xrepo 的依赖包管理 我们新增了一个独立项目 [xrepo-cmake](https://github.com/xmake-io/xrepo-cmake)。 它是一个基于 Xrepo/Xmake 的 C/C++ 包管理器的 CMake 包装器。 这允许使用 CMake 来构建您的项目,同时使用 Xrepo 来管理依赖包。这个项目的部分灵感来自 [cmake-conan](https://github.com/conan-io/cmake-conan)。 此项目的示例用例: * 想要使用 Xrepo 管理包的现有 CMake 项目。 * 必须使用 CMake,但想使用 Xrepo 管理的新项目包。 #### 使用来自官方存储库的包 Xrepo 官方仓库:[xmake-repo](https://github.com/xmake-io/xmake-repo) [xrepo.cmake](https://github.com/xmake-io/xrepo-cmake/blob/main/xrepo.cmake) 提供`xrepo_package`函数来管理包。 ```cmake xrepo_package( "foo 1.2.3" [CONFIGS feature1=true,feature2=false] [MODE debug|release] [OUTPUT verbose|diagnosis|quiet] [DIRECTORY_SCOPE] ) ``` 一些函数参数直接对应于 Xrepo 命令选项。 调用 `xrepo_package(foo)` 后,有两种使用 `foo` 包的方法: * 如果包提供 cmake 模块来查找它,则调用 `find_package(foo)`, 参考 CMake [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) 文档了解更多详情 * 如果包不提供 cmake 模块,`foo_INCLUDE_DIR` 和 `foo_LINK_DIR` 变量将设置为包包含和库路径。使用这些变量在 CMake 代码中设置包含和库路径。 * 如果指定了 `DIRECTORY_SCOPE`,则 `xrepo_package` 将运行以下代码(这样用户只需要在 `target_link_libraries` 中指定库名称) ```cmake include_directories(foo_INCLUDE_DIR) link_directories(foo_LINK_DIR) ``` 这是一个使用 `gflags` 包版本 2.2.2 的示例 `CMakeLists.txt` 由 Xrepo 管理。 ```cmake cmake_minimum_required(VERSION 3.13.0) project(foo) # Download xrepo.cmake if not exists in build directory. if(NOT EXISTS "${CMAKE_BINARY_DIR}/xrepo.cmake") message(STATUS "Downloading xrepo.cmake from https://github.com/xmake-io/xrepo-cmake/") # mirror https://cdn.jsdelivr.net/gh/xmake-io/xrepo-cmake@main/xrepo.cmake file(DOWNLOAD "https://raw.githubusercontent.com/xmake-io/xrepo-cmake/main/xrepo.cmake" "${CMAKE_BINARY_DIR}/xrepo.cmake" TLS_VERIFY ON) endif() # Include xrepo.cmake so we can use xrepo_package function. include(${CMAKE_BINARY_DIR}/xrepo.cmake) # Call `xrepo_package` function to use gflags 2.2.2 with specific configs. xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true") # `xrepo_package` sets `gflags_DIR` variable in parent scope because gflags # provides cmake modules. So we can now call `find_package` to find gflags # package. find_package(gflags CONFIG COMPONENTS shared) ``` #### 使用来自第三个存储库的包 除了从官方维护的存储库安装软件包之外,Xrepo 还可以安装来自第三方包管理器的包,例如 vcpkg/conan/conda/pacman/homebrew/apt/dub/cargo。 关于命令行的使用,我们可以参考文档:[Xrepo命令用法](https://xrepo.xmake.io/#/getting_started#install-packages-from-third-party-package-manager) 我们也可以直接在 cmake 中使用它来安装来自第三方仓库的包,只需将仓库名称添加为命名空间即可。例如:`vcpkg::zlib`, `conan::pcre2` ##### Conan ```cmake xrepo_package("conan::gflags/2.2.2") ``` ##### Conda ```cmake xrepo_package("conda::gflags 2.2.2") ``` ##### Vcpkg ```cmake xrepo_package("vcpkg::gflags") ``` ##### Homebrew ```cmake xrepo_package("brew::gflags") ``` ### Python 模块构建支持 我们可以用这个规则,配合 pybind11 生成 python 库模块,它会调整 python 库的模块名。 ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library") add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` 带有 soabi: ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library", {soabi = true}) add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` ### 新增删除头文件列表接口 通过此接口,可以从 `add_headerfiles` 接口添加的头文件列表中,删除指定的文件,例如: ```lua target("test") add_headerfiles("src/*.h") remove_headerfiles("src/test.h") ``` 上面的例子,可以从`src`目录下添加除`test.h`以外的所有头文件,当然这个也可以通过 `add_headerfiles("src/*.h|test.h")` 来达到相同的目的,但是这种方式更加灵活。 ### 新增 on\_config 配置脚本 在 `xmake config` 执行完成后,Build 之前会执行此脚本,通常用于编译前的配置工作。它与 on\_load 不同的是,on\_load 只要 target 被加载就会执行,执行时机更早。 如果一些配置,无法在 on\_load 中过早配置,那么都可以在 on\_config 中去配置它。 另外,它的执行时机比 before\_build 还要早,大概的执行流程如下: ``` on_load -> after_load -> on_config -> before_build -> on_build -> after_build ``` ### 内置 Github 代理镜像配置 Xmake 提供了一些内置的镜像配置可以直接使用,例如 github 的镜像加速: ```console $ xmake g --proxy_pac=github_mirror.lua ``` 我们不用自己编写 pac.lua,就可以直接使用它来加速 github 源的下载。 ## 更新内容 ### 新特性 * [#1298](https://github.com/xmake-io/xmake/issues/1928): 支持 vcpkg 清单模式安装包,实现安装包的版本选择 * [#1896](https://github.com/xmake-io/xmake/issues/1896): 添加 `python.library` 规则去构建 pybind 模块,并且支持 soabi * [#1939](https://github.com/xmake-io/xmake/issues/1939): 添加 `remove_files`, `remove_headerfiles` 并且标记 `del_files` 作为废弃接口 * 将 on\_config 作为正式的公开接口,用于 target 和 rule * 添加 riscv32/64 支持 * [#1970](https://github.com/xmake-io/xmake/issues/1970): 添加 CMake wrapper 支持在 CMakelists 中去调用 xrepo 集成 C/C++ 包 * 添加内置的 github 镜像加速 pac 代理文件, `xmake g --proxy_pac=github_mirror.lua` ### 改进 * [#1923](https://github.com/xmake-io/xmake/issues/1923): 改进构建 linux 驱动,支持设置自定义 linux-headers 路径 * [#1962](https://github.com/xmake-io/xmake/issues/1962): 改进 armclang 工具链去支持构建 asm * [#1959](https://github.com/xmake-io/xmake/pull/1959): 改进 vstudio 工程生成器 * [#1969](https://github.com/xmake-io/xmake/issues/1969): 添加默认的 option 描述 ### Bugs 修复 * [#1875](https://github.com/xmake-io/xmake/issues/1875): 修复部署生成 Android Qt 程序包失败问题 * [#1973](https://github.com/xmake-io/xmake/issues/1973): 修复合并静态库 * [#1982](https://github.com/xmake-io/xmake/pull/1982): 修复 clang 下对 c++20 子模块的依赖构建 --- --- url: /posts/xmake-update-v2.6.4.md --- ## Introduction of new features ### More flexible package extensions Now, we can inherit all the configuration of an existing package through the `set_base` interface, and then rewrite part of the configuration on this basis. This is usually in the user's own project, it is more useful to modify the built-in package of the official repository of [xmake-repo](https://github.com/xmake-io/xmake-repo), such as: repairing and changing urls, modifying the version list, Install logic and more. For example, modify the url of the built-in zlib package to switch to your own zlib source address. ```lua package("myzlib") set_base("zlib") set_urls("https://github.com/madler/zlib.git") package_end() add_requires("myzlib", {system = false, alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` We can also use it to simply add an alias package. ```lua package("onetbb") set_base("tbb") ``` We can install the tbb package through `add_requires("onetbb")` integration, but the package name is different. ### Package management supports toolchain switching Previously, we limited the toolchains that can only be installed under the cross platform to switch packages. In the new version, we can support the switchover of toolchains under more platforms. E.g: ```bash $ xrepo install --toolchains=clang zlib ``` We can quickly switch to the clang toolchain to compile and install the zlib library on platforms such as linux. We can also switch them in the xmake.lua configuration file. ```lua add_requires("zlib", {configs = {toolchains = "gcc-11"}}) ``` The zlib packages installed by different tool chains will be stored in different directories without interfering with each other, and there will be no link compatibility problems caused by compiler differences. ### Built-in package virtual environment The Xrepo command has already well supported package virtual environment management, `xrepo env shell`, but for complex package environments, users still need to configure an xmake.lua file to manage their own package environment. For example, we need a common development environment shell with common development toolchains such as cmake, python and vs/autoconf by default, and we need to create a configuration file devel.lua by ourselves. ```lua add_requires("cmake") add_requires("python") if is_host("linux", "bsd", "macosx") then add_requires("pkg-config", "autoconf", "automake", "libtool") elseif is_host("windows") then set_toolchains("msvc") end ``` Then, execute the following command to import into the global configuration. ```bash $ xrepo env --add devel.lua ``` In this way, we can load the shell and bind this environment with the following command: ```bash $ xrepo env -b devel shell > cmake --version cmake version 3.19.3 ``` In the new version, we have built in some commonly used environments, which can be viewed through `xrepo env -l`: ```bash $ xrepo env -l - msvc - llvm-mingw - llvm - mingw-w64 -devel - python3 - depot_tools - python2 ``` Among them, devel is also in it, so we only need to execute `xrepo env -b devel shell` to bring up a devel development environment without configuring them yourself. Such as python, msvc, etc. are also some of the more commonly used environments, which can be used directly. Of course, we also support temporarily creating an xmake.lua locally to configure the loading package environment instead of placing it in the global configuration. ### Custom installation package download We can customize the download logic of the package through the new `on_download` interface, which is usually not used, and it is enough to use Xmake's built-in download. If the user builds a private repository and has a more complex authentication mechanism and special processing logic for the download of the package, the internal download logic can be rewritten to achieve this. ```lua on_download(function (package, opt) -- download packages:urls() to opt.sourcedir end) ``` In the opt parameter, you can get the destination source directory `opt.sourcedir` of the downloaded package. We only need to get the package address from `package:urls()` and download it. Then, add some custom processing logic as needed. In addition, you can add download cache processing and so on. ### ASN.1 Program Build Support ASN.1 programs need to use [ASN.1 Compiler](https://github.com/vlm/asn1c) to generate relevant .c files to participate in project compilation. While Xmake provides built-in `add_rules("asn1c")` rules to process `.c` file generation, `add_requires("asn1c")` automatically pulls and integrates ASN.1 compiler tools. Here is a basic configuration example: ```lua add_rules("mode.debug", "mode.release") add_requires("asn1c") target("test") set_kind("binary") add_files("src/*.c") add_files("src/*.asn1") add_rules("asn1c") add_packages("asn1c") ``` For details, see [Complete Example Project](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/asn1c). ### Support for building Swift programs on all platforms Previously, Xmake only supported the construction of Swift programs under macOS with the help of the Xcode toolchain. In the new version, we have also made improvements to allow the swift toolchain to be used independently, and to support the construction of swift programs on linux/windows. The usage is the same as before. ### Supports export of specified symbol list In previous versions, we provided `utils.symbols.export_all` for automatic full symbol export of dll libraries for windows. Although this is very convenient, it can only support windows programs, and the full export does not control the size of the generated dll, and there may be many internal symbols that are not needed at all to be exported. However, the `utils.symbols.export_list` rule provided by our new version can directly define the list of exported symbols in xmake.lua, for example: ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_list", {symbols = { "add", "sub"}}) ``` Alternatively, add a list of exported symbols in the `*.export.txt` file. ```lua target("foo2") set_kind("shared") add_files("src/foo.c") add_files("src/foo.export.txt") add_rules("utils.symbols.export_list") ``` For a complete project example, see: [Export Symbol Example](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c/shared_library_export_list) By specifying symbol export, we can make the generated dynamic library as small as possible, and do not export irrelevant internal symbols at all. In addition, this rule supports linux, macOS and windows, which is more general. Internally it automatically uses .def, version scripts and `--exported_symbols_list` to handle symbol exports. ### Built-in support for linker scripts In the new version, we also have built-in support for linker scripts and version scripts files, we can use `add_files` to add them directly without configuring `add_ldflags("-Txxx.lds")`. Currently `.ld` and `.lds` are supported as linker scripts configuration files to add: ```lua add_rules("mode.debug", "mode.release") target("test") add_deps("foo") set_kind("binary") add_files("src/main.c") add_files("src/main.lds") ``` We also support `.ver`, `.map` suffix files to be added as version script. ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_files("src/foo.map") ``` The content of the foo.map file is as follows: ``` { global: foo; local: *; }; ``` ## Changelog ### New features * [#2011](https://github.com/xmake-io/xmake/issues/2011): Support to inherit base package * Support to build and run xmake on sparc, alpha, powerpc, s390x and sh4 * Add on\_download for package() * [#2021](https://github.com/xmake-io/xmake/issues/2021): Support Swift for linux and windows * [#2024](https://github.com/xmake-io/xmake/issues/2024): Add asn1c support * [#2031](https://github.com/xmake-io/xmake/issues/2031): Support linker scripts and version scripts for add\_files * [#2033](https://github.com/xmake-io/xmake/issues/2033): Catch ctrl-c to get current backtrace for debugging stuck * [#2059](https://github.com/xmake-io/xmake/pull/2059): Add `xmake update --integrate` to integrate for shell * [#2070](https://github.com/xmake-io/xmake/issues/2070): Add built-in xrepo environments * [#2117](https://github.com/xmake-io/xmake/pull/2117): Support to pass toolchains to package for other platforms * [#2121](https://github.com/xmake-io/xmake/issues/2121): Support to export the given symbols list ### Changes * [#2036](https://github.com/xmake-io/xmake/issues/2036): Improve xrepo to install packages from configuration file, e.g. `xrepo install xxx.lua` * [#2039](https://github.com/xmake-io/xmake/issues/2039): Improve filter directory for vs generator * [#2025](https://github.com/xmake-io/xmake/issues/2025): Support phony and headeronly target for vs generator * Improve to find vstudio and codesign speed * [#2077](https://github.com/xmake-io/xmake/issues/2077): Improve vs project generator to support cuda ### Bugs fixed * [#2005](https://github.com/xmake-io/xmake/issues/2005): Fix path.extension * [#2008](https://github.com/xmake-io/xmake/issues/2008): Fix windows manifest * [#2016](https://github.com/xmake-io/xmake/issues/2016): Fix object filename confict for vs project generator --- --- url: /zh/posts/xmake-update-v2.6.4.md --- ## 新特性介绍 ### 更灵活的包扩展 现在,我们可以通过 `set_base` 接口去继承一个已有的包的全部配置,然后在此基础上重写部分配置。 这通常在用户自己的项目中,修改 [xmake-repo](https://github.com/xmake-io/xmake-repo) 官方仓库的内置包比较有用,比如:修复改 urls,修改版本列表,安装逻辑等等。 例如,修改内置 zlib 包的 url,切到自己的 zlib 源码地址。 ```lua package("myzlib") set_base("zlib") set_urls("https://github.com/madler/zlib.git") package_end() add_requires("myzlib", {system = false, alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` 我们也可以用来单纯添加一个别名包。 ```lua package("onetbb") set_base("tbb") ``` 我们可以通过 `add_requires("onetbb")` 集成安装 tbb 包,只是包名不同而已。 ### 包管理支持工具链切换 之前,我们限制了只能在 cross 平台下切换包安装的工具链,新版本中,我们可以支持更多平台下,对工具链的切换。 例如: ```bash $ xrepo install --toolchains=clang zlib ``` 我们可以在 linux 等平台上,快速切换到 clang 工具链编译安装 zlib 库。 我们也可以在 xmake.lua 的配置文件中去切换他们。 ```lua add_requires("zlib", {configs = {toolchains = "gcc-11"}}) ``` 不同的工具链安装的 zlib 包,会被分别存储在不同目录,互不干扰,不会存在编译器差异导致的链接兼容问题。 ### 内置的包虚拟环境 Xrepo 命令之前已经很好的支持了包虚拟环境管理,`xrepo env shell`,但是对于复杂的包环境,还是需要用户自己配置一个 xmake.lua 文件,用于管理自己的包环境。 例如,我们需要一个常用的开发环境 shell,默认带有 cmake, python 和 vs/autoconf 等常用的开发工具链,我们需要自己起一个配置文件 devel.lua。 ```lua add_requires("cmake") add_requires("python") if is_host("linux", "bsd", "macosx") then add_requires("pkg-config", "autoconf", "automake", "libtool") elseif is_host("windows") then set_toolchains("msvc") end ``` 然后,执行下面的命令去导入到全局配置。 ```bash $ xrepo env --add devel.lua ``` 这样,我们可以通过下面的命令,去加载 shell 绑定这个环境: ```bash $ xrepo env -b devel shell > cmake --version cmake version 3.19.3 ``` 而在新版本中,我们内置了一些常用的环境,可以通过 `xrepo env -l` 查看: ```bash $ xrepo env -l - msvc - llvm-mingw - llvm - mingw-w64 - devel - python3 - depot_tools - python2 ``` 其中 devel 也在里面,所以,我们只需要执行 `xrepo env -b devel shell` 就可以带起一个 devel 开发环境,而不需要自己配置它们。 像 python, msvc 等也都是一些比较常用的环境,都可以直接使用。 当然,我们也支持临时在本地创建一个 xmake.lua 来配置加载包环境,而不放置到全局配置中去。 ### 自定义安装包下载 我们可以通过新增的 `on_download` 接口,自定义包的下载逻辑,通常用不到,使用 Xmake 的内置下载就足够了。 如果用户自建私有仓库,对包的下载有更复杂的鉴权机制,特殊处理逻辑,那么可以重写内部的下载逻辑来实现。 ```lua on_download(function (package, opt) -- download packages:urls() to opt.sourcedir end) ``` opt 参数里面,可以获取到下载包的目的源码目录 `opt.sourcedir`,我们只需要从 `package:urls()` 获取到包地址,下载下来就可以了。 然后,根据需要,添加一些自定义的处理逻辑。另外,自己可以添加下载缓存处理等等。 ### ASN.1 程序构建支持 ASN.1 程序,需要借助 [ASN.1 Compiler](https://github.com/vlm/asn1c) 去生成相关的 .c 文件参与项目编译。 而 Xmake 内置提供了 `add_rules("asn1c")` 规则去处理 `.c` 文件生成,`add_requires("asn1c")` 自动拉取集成 ASN.1 编译器工具。 下面是一个基础的配置例子: ```lua add_rules("mode.debug", "mode.release") add_requires("asn1c") target("test") set_kind("binary") add_files("src/*.c") add_files("src/*.asn1") add_rules("asn1c") add_packages("asn1c") ``` 具体见 [完整例子工程](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/asn1c)。 ### 支持全平台构建 Swift 程序 之前,Xmake 仅支持 macOS 下借助 Xcode 工具链实现对 Swift 程序的构建,新版本中,我们也进行了改进,可以独立使用 swift 工具链,支持在 linux/windows 上构架 swift 程序,用法跟之前相同。 ### 支持指定符号列表导出 在之前的版本中,我们提供了 `utils.symbols.export_all` 对 windows 的 dll 库实现自动的完整符号导出。 这尽管很方便,但只能支持 windows 程序,并且全量导出对生成的 dll 大小不好控制,有可能会存在不少根本不需要的内部符号被导出。 而,我们新版本提供的 `utils.symbols.export_list` 规则,可以在 xmake.lua 里面直接定义导出的符号列表,例如: ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_list", {symbols = { "add", "sub"}}) ``` 或者,在 `*.export.txt` 文件中添加导出的符号列表。 ```lua target("foo2") set_kind("shared") add_files("src/foo.c") add_files("src/foo.export.txt") add_rules("utils.symbols.export_list") ``` 完整的工程例子见:[导出符号例子](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c/shared_library_export_list) 通过指定符号导出,我们可以使得生成的动态库尽可能的小,无关的内部符号完全不去导出它们,另外这个规则支持 linux, macOS 和 windows,更加的通用。 它内部会自动使用 .def, version scripts 和 `--exported_symbols_list` 去处理符号导出。 ### 内置支持 linker scripts 新版中,我们也内置了 对 linker scripts 和 version scripts 文件的支持,我们可以使用 `add_files` 直接添加它们,而不需要配置 `add_ldflags("-Txxx.lds")`。 当前支持 `.ld` 和 `.lds` 作为 linker scripts 配置文件来添加: ```lua add_rules("mode.debug", "mode.release") target("test") add_deps("foo") set_kind("binary") add_files("src/main.c") add_files("src/main.lds") ``` 我们也支持 `.ver`, `.map` 后缀文件作为 version script 来添加。 ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_files("src/foo.map") ``` foo.map 文件内容如下: ``` { global: foo; local: *; }; ``` ## 更新内容 ### 新特性 * [#2011](https://github.com/xmake-io/xmake/issues/2011): 支持继承和局部修改官方包,例如对现有的包更换 urls 和 versions * 支持在 sparc, alpha, powerpc, s390x 和 sh4 上编译运行 xmake * 为 package() 添加 on\_download 自定义下载 * [#2021](https://github.com/xmake-io/xmake/issues/2021): 支持 Linux/Windows 下构建 Swift 程序 * [#2024](https://github.com/xmake-io/xmake/issues/2024): 添加 asn1c 支持 * [#2031](https://github.com/xmake-io/xmake/issues/2031): 为 add\_files 增加 linker scripts 和 version scripts 支持 * [#2033](https://github.com/xmake-io/xmake/issues/2033): 捕获 ctrl-c 去打印当前运行栈,用于调试分析卡死问题 * [#2059](https://github.com/xmake-io/xmake/pull/2059): 添加 `xmake update --integrate` 命令去整合 shell * [#2070](https://github.com/xmake-io/xmake/issues/2070): 添加一些内置的 xrepo env 环境配置 * [#2117](https://github.com/xmake-io/xmake/pull/2117): 支持为任意平台传递工具链到包 * [#2121](https://github.com/xmake-io/xmake/issues/2121): 支持导出指定的符号列表,可用于减少动态库的大小 ### 改进 * [#2036](https://github.com/xmake-io/xmake/issues/2036): 改进 xrepo 支持从配置文件批量安装包,例如:`xrepo install xxx.lua` * [#2039](https://github.com/xmake-io/xmake/issues/2039): 改进 vs generator 的 filter 目录展示 * [#2025](https://github.com/xmake-io/xmake/issues/2025): 支持为 phony 和 headeronly 目标生成 vs 工程 * 优化 vs 和 codesign 的探测速度 * [#2077](https://github.com/xmake-io/xmake/issues/2077): 改进 vs 工程生成器去支持 cuda ### Bugs 修复 * [#2005](https://github.com/xmake-io/xmake/issues/2005): 修复 path.extension * [#2008](https://github.com/xmake-io/xmake/issues/2008): 修复 windows manifest 文件编译 * [#2016](https://github.com/xmake-io/xmake/issues/2016): 修复 vs project generator 里,对象文件名冲突导致的编译失败 --- --- url: /posts/xmake-update-v2.6.5.md --- ## Introduction of new features ### Remote Compilation Support The new version provides remote compilation support, which allows us to compile code on a remote server, run and debug remotely. The server can be deployed on Linux/MacOS/Windows to enable cross-platform compilation, e.g. compile and run Windows programs on Linux and macOS/Linux programs on Windows. It is more stable and smoother to use than ssh remote login compilation, no lagging of ssh terminal input due to network instability, and allows for quick local editing of code files. We can even seamlessly implement remote compilation in editors and IDEs such as vs/sublime/vscode/idea without relying on the IDE's own support for remote compilation. #### Start service ```console $ xmake service : listening 0.0.0.0:9096 ... ``` We can also turn on the service while displaying back detailed log messages. ```console $ xmake service -vD : listening 0.0.0.0:9096 ... ``` #### Start the service in Daemon mode ```console $ xmake service --start $ xmake service --restart $ xmake service --stop ``` #### Configure the server We start by running the `xmake service` command, which automatically generates a default `service.conf` configuration file, stored in `~/.xmake/service.conf`. Then, we edit it to fix the server's listening port (optional). ```lua { logfile = "/Users/ruki/.xmake/service/logs.txt", remote_build = { server = { listen = "0.0.0.0:9096" } } } ``` #### Configure the client We still edit this file `~/.xmake/service.conf` to configure the address of the server to which the client needs to connect. ```lua { logfile = "/Users/ruki/.xmake/service/logs.txt", remote_build = { client = { connect = "192.168.56.101:9096", } } } ``` #### Import the given configuration file We can also import the given configuration file by using the following command. ```console $ xmake service --config=/tmp/service.conf ``` #### Connect to the remote server Next, we just need to go into the root directory of the project we need to compile remotely and execute the `xmake service --connect` command to make the connection. ```console $ xmake create test $ cd test $ xmake service --connect : connect 192.168.56.110:9096 ... : connected! : sync files in 192.168.56.110:9096 ... Scanning files ... Comparing 3 files ... [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files . Uploading files with 1372 bytes ... : sync files ok! ``` #### Remote build project Once the connection is successful, we can build remotely as if we were building locally as normal. ````console $ xmake : run xmake in 192.168.56.110:9096 ... checking for platform ... macosx checking for architecture ... ... x86_64 ... checking for Xcode directory ... /Applications/Xcode.app checking for Codesign Identity of Xcode ... Apple Development: waruqi@gmail.com (T3NA4MRVPU) ... checking for SDK version of Xcode for macosx (x86_64) ... 11.3 ... checking for Minimal target version of Xcode for macosx (x86_64) ... 11.4 [ 25%]: ccache compiling.release src/main.cpp [ 50%]: linking.release test [ 100%]: build ok! : run command ok! ```'' #### Running the target program remotely We can also run a debug-compiled target program remotely, just as we can run a debug locally. ```console $ xmake run : run xmake run in 192.168.56.110:9096 ... hello world! : run command ok! ```` #### Rebuild project remotely ````console $ xmake -rv : run xmake -rv in 192.168.56.110:9096 ... [ 25% ]: ccache compiling.release src/main.cpp /usr/local/bin/ccache /usr/bin/xcrun -sdk macosx clang -c -Qunused-arguments -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/ Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp [ 50%]: linking.release test "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/test build/.objs/test/macosx/x86_64/release/src/main.cpp.o -arch x86_ 64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3 .sdk -stdlib=libc++ -Wl,-x -lz [100%]: build ok! : run command ok! ```'' #### Remote configuration of build parameters ```console $ xmake f --xxx --yy ```` #### Manually synchronise project files When you connect, the code is automatically synchronised once, and you can execute this command to manually synchronise the changed files if the code changes later. ```console $ xmake service --sync : sync files in 192.168.56.110:9096 ... Scanning files ... Comparing 3 files ... [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files . Uploading files with 1372 bytes ... : sync files ok! ``` #### Disconnect from a remote This only affects the current project, other projects can still connect and build at the same time. ```console $ xmake service --disconnect : disconnect 192.168.56.110:9096 ... : disconnected! ``` #### View server logs ```console $ xmake service --logs ``` #### Clear the remote service cache and build files We can also manually clean up any cache and build generated files for the remote. ```console $ cd projectdir $ xmake service --clean ``` ### Improve Cargo package dependencies In previous versions we have been able to integrate each cargo package individually via \`\`add\_requires("cargo::base64")\` for compiling rust projects, and for mixed compilation with C/C++, e.g. ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` But there is a problem with the above approach. If there are many dependencies and several dependencies all share a dependency on the same child dependency, then there will be a redefinition problem, so if we use the full Cargo.toml to manage the dependencies we won't have this problem. For example ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::test", {configs = {cargo_toml = path.join(os.projectdir(), "Cargo.toml")}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::test") ``` We can then integrate all the required dependencies in Cargo.toml, leaving Rust to analyse the dependencies itself and avoid duplicate child dependency conflicts. For a full example see: [cargo\_deps\_with\_toml](https://github.com/xmake-io/xmake/blob/dev/tests/projects/rust/cargo_deps_with_toml/xmake.lua) Of course, if the user has a single dependency, then the previous integration approach is still perfectly usable. #### Why use Xmake to compile Rust? At this point, one might ask why we need to configure xmake.lua when we are already using Cargo.toml and Cargo, and why not just compile Cargo? If we are developing a C/C++ project in Xmake, but need to introduce some Rust submodules for use in the C/C++ project, this is a quick and easy way to call Rust libraries and code in C/C++. For more instructions on calling Rust code libraries in C/C++, see: [Calling Rust in C/C++ using cxxbridge](https://xmake.io) ### Support for source file grouping In this new version, we provide a new interface `add_filegroups` for grouping source files for presentation of project files generated by the vs/vsxmake/cmakelists generator. If you don't set up grouping, Xmake will also display them in a tree-like pattern by default, but there are some extreme cases where the directory hierarchy doesn't display very well, e.g. ```lua target("test") set_kind("binary") add_files("... /... /... /... /src/**.cpp") ``` ![](/assets/img/manual/filegroup1.png) Two main presentation modes are currently supported. * plain: flat mode * tree: tree display, this is also the default mode It also supports the grouping of files added by `add_headerfiles`. #### Set the file group and specifies the root directory ```lua target("test") set_kind("binary") add_files(". /... /... /... /src/**.cpp") add_filegroups("group1/group2", {rootdir = "... /... /... /... /"}) ``` ![](/assets/img/manual/filegroup2.png) #### Set the file group and specifies the file matching pattern ```lua target("test") set_kind("binary") add_files("... /... /... /... /src/**.cpp") add_filegroups("group1/group2", {rootdir = "... /... /... /... /", files = {"src/**.cpp"}}) ``` #### Show files as flat mode In this mode, all source files ignore the nested directory hierarchy and are displayed at the same level under the grouping. ```lua target("test") set_kind("binary") add_files(". /... /... /... /src/**.cpp") add_filegroups("group1/group2", {rootdir = "... /... /... /... /", mode = "plain"}) ``` ![](/assets/img/manual/filegroup3.png) ### Package versioning support for Git Commit Xmake's package dependency management interface `add_requires` supports versioning semantics, branch selection, e.g. ```lua add_requires("tbox 1.6.1") add_requires("tbox >= 1.6.1") add_requires("tbox master") ``` However, in previous versions, we didn't support selecting versions from Git Commit, and now we do. ```lua add_requires("tbox e807230557aac69e4d583c75626e3a7ebdb922f8") ``` As long as, this package is configured with a Git url, you can select the version from Commit. ### Better support for iOS simulator compilation If you want to compile a target application for the iOS platform, you can previously use the following configuration to compile the real and emulator versions of the application separately, simply by switching arch. ```bash $ xmake f -p iphoneos [-a armv7|armv7s|arm64|i386|x86_64] $ xmake ``` However, since the emulator on M1 devices also supports the arm64 architecture, it was no longer possible to distinguish an emulator from an arch. Therefore, in this new version, we have added a new parameter to distinguish between emulator and emulator targets. ```bash $ xmake f -p iphoneos --appledev=simulator $ xmake f -p watchos --appledev=simulator $ xmake f -p appletvos --appledev=simulator ``` And if you don't specify the `--appledev=` argument, the default is to compile the real program, but of course the previous modes are fully compatible. ## Changelog ### New features * [#2138](https://github.com/xmake-io/xmake/issues/2138): Support template package * [#2185](https://github.com/xmake-io/xmake/issues/2185): Add `--appledev=simulator` to improve apple simulator support * [#2227](https://github.com/xmake-io/xmake/issues/2227): Improve cargo package with Cargo.toml file * Improve `add_requires` to support git commit as version * [#622](https://github.com/xmake-io/xmake/issues/622): Support remote compilation * [#2282](https://github.com/xmake-io/xmake/issues/2282): Add `add_filegroups` to support file group for vs/vsxmake/cmake generator ### Changes * [#2137](https://github.com/xmake-io/xmake/pull/2137): Improve path module * Reduce 50% xmake binary size on macOS * Improve tools/autoconf,cmake to support toolchain switching. * [#2221](https://github.com/xmake-io/xmake/pull/2221): Improve registry api to support unicode * [#2225](https://github.com/xmake-io/xmake/issues/2225): Support to parse import dependencies for protobuf * [#2265](https://github.com/xmake-io/xmake/issues/2265): Sort CMakeLists.txt * Speed up `os.files` ### Bugs fixed * [#2233](https://github.com/xmake-io/xmake/issues/2233): Fix c++ modules deps --- --- url: /zh/posts/xmake-update-v2.6.5.md --- ## 新特性介绍 ### 远程编译支持 新版本提供了远程编译支持,我们可以通过它可以远程服务器上编译代码,远程运行和调试。 服务器可以部署在 Linux/MacOS/Windows 上,实现跨平台编译,例如:在 Linux 上编译运行 Windows 程序,在 Windows 上编译运行 macOS/Linux 程序。 相比 ssh 远程登入编译,它更加的稳定,使用更加流畅,不会因为网络不稳定导致 ssh 终端输入卡顿,也可以实现本地快速编辑代码文件。 甚至我们可以在 vs/sublime/vscode/idea 等编辑器和IDE 中无缝实现远程编译,而不需要依赖 IDE 本身对远程编译的支持力度。 #### 开启服务 ```console $ xmake service : listening 0.0.0.0:9096 .. ``` 我们也可以开启服务的同时,回显详细日志信息。 ```console $ xmake service -vD : listening 0.0.0.0:9096 .. ``` #### 以 Daemon 模式开启服务 ```console $ xmake service --start $ xmake service --restart $ xmake service --stop ``` #### 配置服务端 我们首先,运行 `xmake service` 命令,它会自动生成一个默认的 `service.conf` 配置文件,存储到 `~/.xmake/service.conf`。 然后,我们编辑它,修复服务器的监听端口(可选)。 ```lua { logfile = "/Users/ruki/.xmake/service/logs.txt", remote_build = { server = { listen = "0.0.0.0:9096" } } } ``` #### 配置客户端 我们还是编辑这个文件 `~/.xmake/service.conf`,配置客户端需要连接的服务器地址。 ```lua { logfile = "/Users/ruki/.xmake/service/logs.txt", remote_build = { client = { connect = "192.168.56.101:9096", } } } ``` #### 导入给定的配置文件 我们也可以通过下面的命令,导入指定的配置文件。 ```console $ xmake service --config=/tmp/service.conf ``` #### 连接远程的服务器 接下来,我们只需要进入需要远程编译的工程根目录,执行 `xmake service --connect` 命令,进行连接。 ```console $ xmake create test $ cd test $ xmake service --connect : connect 192.168.56.110:9096 .. : connected! : sync files in 192.168.56.110:9096 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` #### 远程构建工程 连接成功后,我们就可以像正常本地编译一样,进行远程编译。 ```console $ xmake : run xmake in 192.168.56.110:9096 .. checking for platform ... macosx checking for architecture ... x86_64 checking for Xcode directory ... /Applications/Xcode.app checking for Codesign Identity of Xcode ... Apple Development: waruqi@gmail.com (T3NA4MRVPU) checking for SDK version of Xcode for macosx (x86_64) ... 11.3 checking for Minimal target version of Xcode for macosx (x86_64) ... 11.4 [ 25%]: ccache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! : run command ok! ``` #### 远程运行目标程序 我们也可以像本地运行调试那样,远程运行调试编译的目标程序。 ```console $ xmake run : run xmake run in 192.168.56.110:9096 .. hello world! : run command ok! ``` #### 远程重建工程 ```console $ xmake -rv : run xmake -rv in 192.168.56.110:9096 .. [ 25%]: ccache compiling.release src/main.cpp /usr/local/bin/ccache /usr/bin/xcrun -sdk macosx clang -c -Qunused-arguments -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp [ 50%]: linking.release test "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/test build/.objs/test/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -stdlib=libc++ -Wl,-x -lz [100%]: build ok! : run command ok! ``` #### 远程配置编译参数 ```console $ xmake f --xxx --yy ``` #### 手动同步工程文件 连接的时候,会自动同步一次代码,后期代码改动,可以执行此命令来手动同步改动的文件。 ```console $ xmake service --sync : sync files in 192.168.56.110:9096 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` #### 断开远程连接 针对当前工程,断开连接,这仅仅影响当前工程,其他项目还是可以同时连接和编译。 ```console $ xmake service --disconnect : disconnect 192.168.56.110:9096 .. : disconnected! ``` #### 查看服务器日志 ```console $ xmake service --logs ``` #### 清理远程服务缓存和构建文件 我们也可以手动清理远程的任何缓存和构建生成的文件。 ```console $ cd projectdir $ xmake service --clean ``` ### 改进 Cargo 包依赖 在之前的版本中,我们已经可以通过 `add_requires("cargo::base64")` 去单独集成每个 cargo 包,用于编译 rust 项目,以及与 C/C++ 的混合编译,例如: ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` 但是上面的方式会有一个问题: 如果依赖很多,并且有几个依赖都共同依赖了相同的子依赖,那么会出现重定义问题,因此如果我们使用完整的 Cargo.toml 去管理依赖就不会存在这个问题。 例如: ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::test", {configs = {cargo_toml = path.join(os.projectdir(), "Cargo.toml")}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::test") ``` 然后,我们就可以在 Cargo.toml 中集成所有需要的依赖,让 Rust 自己去分析依赖关系,避免重复的子依赖冲突。 完整例子见:[cargo\_deps\_with\_toml](https://github.com/xmake-io/xmake/blob/dev/tests/projects/rust/cargo_deps_with_toml/xmake.lua) 当然,如果用户的依赖比较单一,那么之前的集成方式还是完全可用。 #### 为什么使用 Xmake 编译 Rust? 这个时候,肯定会有人问,既然都用了 Cargo.toml 和 Cargo 了,为什么还要在 xmake.lua 中去配置呢,直接 Cargo 编译不就好了么。 如果我们是在用 Xmake 开发 C/C++ 项目,但是需要引入一些 Rust 子模块给 C/C++ 项目使用,那么就可以借助这种方式,快速方便地在 C/C++ 中调用 Rust 库和代码。 更多关于 C/C++ 中调用 Rust 代码库的说明,见:[使用 cxxbridge 在 C/C++ 中调用 Rust](https://xmake.io/zh/) ### 支持源文件分组 新版本,我们提供了一个新接口 `add_filegroups`,用于对 vs/vsxmake/cmakelists generator 生成的工程文件进行源文件分组展示。 如果不设置分组展示,Xmake 也会默认按照树状模式展示,但是有些极端情况下,目录层级显示不是很好,例如: ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") ``` ![](/assets/img/manual/filegroup1.png) 目前主要支持两种展示模式: * plain: 平坦模式 * tree: 树形展示,这也是默认模式 另外,它也支持对 `add_headerfiles` 添加的文件进行分组。 #### 设置分组并指定根目录 ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") add_filegroups("group1/group2", {rootdir = "../../../../"}) ``` ![](/assets/img/manual/filegroup2.png) #### 设置分组并指定文件匹配模式 ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") add_filegroups("group1/group2", {rootdir = "../../../../", files = {"src/**.cpp"}}) ``` #### 作为平坦模式展示 这种模式下,所有源文件忽略嵌套的目录层级,在分组下同一层级展示。 ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") add_filegroups("group1/group2", {rootdir = "../../../../", mode = "plain"}) ``` ![](/assets/img/manual/filegroup3.png) ### 包版本选择支持 Git Commit Xmake 的包依赖管理接口 `add_requires` 支持版本语义选择,分支选择,例如: ```lua add_requires("tbox 1.6.1") add_requires("tbox >=1.6.1") add_requires("tbox master") ``` 但是,之前的版本,我们还不支持从 Git Commit 中选择版本,而现在我们也支持上了。 ```lua add_requires("tbox e807230557aac69e4d583c75626e3a7ebdb922f8") ``` 只要,这个包的配置中带有 Git url,就能从 Commit 中选择版本。 ### 更好地支持 iOS 模拟器编译 如果要编译 iOS 平台目标程序,之前可以使用如下配置,仅仅通过切换 arch,就能分别编译真机,模拟器版本程序。 ```bash $ xmake f -p iphoneos [-a armv7|armv7s|arm64|i386|x86_64] $ xmake ``` 但是由于 M1 设备上模拟器也支持 arm64 架构,因此之前单纯从 arch 去区分是否为模拟器,已无法满足需求。 因此,在新版本中,我们新增了一个参数配置去区分是否为模拟器目标。 ```bash $ xmake f -p iphoneos --appledev=simulator $ xmake f -p watchos --appledev=simulator $ xmake f -p appletvos --appledev=simulator ``` 而如果没有指定 `--appledev=` 参数,默认就是编译真机程序,当然,之前的模式也是完全兼容的。 ## 更新内容 ### 新特性 * [#2138](https://github.com/xmake-io/xmake/issues/2138): 支持模板包 * [#2185](https://github.com/xmake-io/xmake/issues/2185): 添加 `--appledev=simulator` 去改进 Apple 模拟器目标编译支持 * [#2227](https://github.com/xmake-io/xmake/issues/2227): 改进 cargo 包,支持指定 Cargo.toml 文件 * 改进 `add_requires` 支持 git command 作为版本 * [#622](https://github.com/xmake-io/xmake/issues/622): 支持远程编译 * [#2282](https://github.com/xmake-io/xmake/issues/2282): 添加 `add_filegroups` 接口为 vs/vsxmake/cmake generator 增加文件组支持 ### 改进 * [#2137](https://github.com/xmake-io/xmake/pull/2137): 改进 path 模块 * macOS 下,减少 50% 的 Xmake 二进制文件大小 * 改进 tools/autoconf,cmake 去更好地支持工具链切换 * [#2221](https://github.com/xmake-io/xmake/pull/2221): 改进注册表 api 去支持 unicode * [#2225](https://github.com/xmake-io/xmake/issues/2225): 增加对 protobuf 的依赖分析和构建支持 * [#2265](https://github.com/xmake-io/xmake/issues/2265): 排序 CMakeLists.txt * 改进 os.files 的文件遍历速度 ### Bugs 修复 * [#2233](https://github.com/xmake-io/xmake/issues/2233): 修复 c++ modules 依赖 --- --- url: /posts/xmake-update-v2.6.6.md --- ## Introduction of new features In this version, we have added a lot of heavyweight new features: * Distributed compilation * Local compilation cache * Remote compilation cache With these features, we can compile large C/C++ projects faster. In addition, they are completely cross-platform, support not only gcc/clang but also msvc, and there is no any third-party dependency except the compiler, which is very convenient to use. Therefore, using Xmake is equivalent to using `distcc/ccache/sccache` at the same time. Compared with these third-party tools, Xmake fully supports Windows and msvc, which eliminates platform differences, independent process calls, and the overhead of additional daemon processes. In addition to these features, the new version of Xmake also adds support for compiling Keil/C51 projects, as well as support for the nvc/nvc++/nvfortran compilers in the nvidia-hpc-sdk toolchain. ### Support user authentication for remote compilation In the last version, we initially supported remote compilation, but did not provide user authentication support, which would bring some security issues. Therefore, in this version, we added user authentication support. At present, Xmake mainly provides the following authentication mechanisms. In addition, it is also effective for distributed compilation and remote caching. 1. Token authentication 2. Password authentication 3. Trusted host verification #### Token authentication This is also the default recommended method, which is more secure, more convenient to configure and connect, and does not need to enter a password every time you connect. When we execute the `xmake service` command, a server and client configuration file will be generated by default, and a default token will be automatically generated, so the local direct connection does not require any configuration. ##### Server authentication configuration The server can configure multiple tokens for authorizing connections to different user hosts. Of course, we can also share single token. ```bash $ cat ~/.xmake/service/server.conf { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f" } } ``` ##### Client authentication configuration The client only needs to add the token on the server to the corresponding client configuration. ```bash $ cat ~/.xmake/service/client.conf { remote_build = { connect = "127.0.0.1:9691", token = "e438d816c95958667747c318f1532c0f" } } ``` ##### Manually generate new token We can also execute the following command to manually generate a new token and add it to the server configuration ourselves. ```bash $ xmake service --gen-token New token a7b9fc2d3bfca1472aabc38bb5f5d612 is generated! ``` #### Password authentication We also provide password authentication. Compared with token authentication, it requires users to enter a password every time they connect, and can only be connected after the verification is passed. ##### Server authentication configuration For password authentication, we do not need to manually configure the token, just execute the following command to add a user. During the adding process, the user will be prompted to enter a password. ```bash $ xmake service --add-user=ruki Please input user ruki password: 123456 Add user ruki ok! ``` Then, xmake will generate a new token from the username and password and add it to the token list of the server configuration. ```bash $ cat ~/.xmake/service/server.conf { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f", "7889e25402413e93fd37395a636bf942" } } ``` Of course, we can also delete the specified user and password. ```bash $xmake service --rm-user=ruki Please input user ruki password: 123456 Remove user ruki ok! ``` ##### Client authentication configuration For the client, we no longer need to set the token of the server. We only need to add the user name that needs to be connected in the connection configuration to enable password authentication. The format is: `user@address:port` ```bash $ cat ~/.xmake/service/client.conf { remote_build = { connect = "root@127.0.0.1:9691" } } ``` If the username is removed and the token is not configured, it is anonymous mode. If the server is not configured with a token, the authentication is completely disabled and the connection is made directly. #### Trusted host verification In addition, in order to further improve security, we also provide server-side trusted host verification. If the server-configured known\_hosts list is configured with the ip address of the client host that can be connected. Then only these hosts can successfully connect to this server, and other hosts' connections to it will be prompted to be untrusted and refuse the connection, even if token and password authentication are OK. ```bash $ cat ~/.xmake/service/server.conf { logfile = "/Users/ruki/.xmake/service/logs.txt", server = { tokens = { "4b928c7563a0cba10ff4c3f5ca0c8e24" }, known_hosts = { "127.0.0.1", "xx.xx.xx.xx"} } } ``` #### Connect to remote server Next, we only need to enter the root directory of the project that needs to be compiled remotely, and execute the `xmake service --connect` command to connect. If it is the token authentication mode, then no additional password input is required, and the connection is directly connected. ```console $ xmake create test $ cd test $ xmake service --connect : connect 192.168.56.110:9091 .. : connected! : sync files in 192.168.56.110:9091 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` If it is password authentication, the user will be prompted to enter the password to continue the connection. ```bash $ xmake service --connect Please input user root password: 000000 : connect 127.0.0.1:9691 .. : connected! : sync files in 127.0.0.1:9691 .. Scanning files .. Comparing 3 files .. [+]: xmake.lua [+]: .gitignore [+]: src/main.cpp 3 files has been changed! Archiving files .. Uploading files with 1591 bytes .. : sync files ok! ``` If the password is incorrect, an error message will be displayed. ```bash $ xmake service --connect Please input user root password: 123 : connect 127.0.0.1:9691 .. : connect 127.0.0.1:9691 failed, user and password are incorrect! ``` ### Distributed compilation support Xmake provides a built-in distributed compilation service, usually it can cooperate with local compilation cache and remote compilation cache to achieve optimal compilation acceleration. Also, it is fully cross-platform supported, we not only support gcc/clang, but also Windows and msvc well. For cross-compilation, as long as the cross-toolchain supports, we do not require the system environment of the server. Even if the server resources of linux, macOS and Windows are mixed, distributed compilation can be well realized. #### Start service We can specify the `--distcc` parameter to enable the distributed compilation service. Of course, if this parameter is not specified, xmake will enable all server-configured services by default. ```console $ xmake service --distcc : listening 0.0.0.0:9093 .. ``` We can also start the service and echo detailed log information. ```console $ xmake service --distcc -vD : listening 0.0.0.0:9093 .. ``` #### Start the service in daemon mode ```console $ xmake service --distcc --start $ xmake service --distcc --restart $ xmake service --distcc --stop ``` #### Configure the server We first, run the `xmake service` command, it will automatically generate a default `server.conf` configuration file, stored in `~/.xmake/service/server.conf`. ```bash $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. an token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9693 .. ``` Then, we edit it, fixing the server's listening port (optional). ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` #### Configure the client The client configuration file is in `~/.xmake/service/client.conf`, where we can configure the server address that the client needs to connect to. We can configure multiple server addresses and corresponding tokens in the hosts list. For distributed compilation, it is recommended to use the token authentication mode, because the password mode requires a password to be entered for each server connection, which is very cumbersome. ```console $cat ~/.xmake/service/client.conf { distcc_build = { hosts = { { connect = "127.0.0.1:9693", token = "590234653af52e91b9e438ed860f1a2b" } } } } ``` #### Connect to the server After configuring the authentication and server address, you can enter the following command to connect the current project to the configured server. We need to enter `--distcc` when connecting to specify that only distributed services are connected. ```bash $ cd projectdir $ xmake service --connect --distcc : connect 127.0.0.1:9693 .. : 127.0.0.1:9693 connected! ``` We can also connect to multiple services at the same time, such as distributed compilation and remote compilation cache services. ```sh $ xmake service --connect --distcc --ccache ``` :::NOTE If there is no parameter, the default connection is the remote compilation service. ::: #### Distributed compilation project After connecting to the server, we can perform distributed compilation like normal local compilation, for example: ```bash $ xmake ... [ 93%]: ccache compiling.release src/demo/network/unix_echo_client.c ----> local job [ 93%]: ccache compiling.release src/demo/network/ipv6.c [ 93%]: ccache compiling.release src/demo/network/ping.c [ 93%]: distcc compiling.release src/demo/network/unix_echo_server.c. ----> distcc job [93%]: distcc compiling.release src/demo/network/http.c [ 93%]: distcc compiling.release src/demo/network/unixaddr.c [ 93%]: distcc compiling.release src/demo/network/ipv4.c [ 94%]: distcc compiling.release src/demo/network/ipaddr.c [94%]: distcc compiling.release src/demo/math/fixed.c [94%]: distcc compiling.release src/demo/libm/float.c [ 95%]: ccache compiling.release src/demo/libm/double.c [ 95%]: ccache compiling.release src/demo/other/test.cpp [ 98%]: archiving.release libtbox.a [99%]: linking.release demo [100%]: build ok! ``` Among them, the words with distcc are remote compilation tasks, and the others are local compilation tasks. By default, xmake also enables local compilation caching to cache distributed compilation results to avoid frequent requests to the server. In addition, we can also open the remote compilation cache and share the compilation cache with others to further accelerate the compilation of multi-person collaborative development. #### Disconnect server ```bash $ xmake service --disconnect --distcc ``` #### Specify the number of parallel compilation tasks Let's briefly introduce the number of parallel tasks currently calculated by default based on the number of host cpu cores: ```lua local default_njob = math.ceil(ncpu * 3 / 2) ``` Therefore, if distributed compilation is not enabled, the default maximum number of parallel compilation tasks is this `default_njob`. If distributed compilation is enabled, the default number of parallel compilation tasks is: ```lua local maxjobs = default_njob + server_count * server_default_njob ``` ##### Modify the number of local parallel tasks We only need to pass `-jN` to specify the number of local parallel tasks, but it will not affect the number of parallel tasks on the server side. ```bash $ xmake -jN ``` ##### Modify the number of parallel tasks on the server side If you want to modify the number of parallel tasks on the server, you need to modify the configuration file of the client. ```bash $cat ~/.xmake/service/client.conf { distcc_build = { hosts = { { connect = "127.0.0.1:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 8 <------- modify here }, { connect = "192.168.01:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 4 } } } } ``` For each server host, add the `njob = N` parameter configuration to specify the number of parallel jobs that this server can provide. #### Distributed compilation of Android projects The distributed compilation service provided by xmake is completely cross-platform and supports Windows, Linux, macOS, Android, iOS and even cross-compilation. If you want to compile the Android project, you only need to add the `toolchains` toolchain configuration in the server configuration, and provide the path of the NDK. ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", toolchains = { ndk = { ndk = "~/files/android-ndk-r21e" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` Then, we can compile the Android project in a distributed way like normal local compilation, and even configure multiple Windows, macOS, Linux and other different server hosts as resources of the distributed compilation service to compile it. Just download the NDK for the corresponding platform. ```bash $ xmake f -p android --ndk=~/files/xxxx $ xmake ``` #### Distributed compilation of iOS projects Compiling iOS projects is easier, because Xmake can usually automatically detect Xcode, so just switch the platform to ios like a normal local. ```bash $ xmake f -p iphoneos $ xmake ``` #### Distributed cross compilation configuration If we want to distribute cross-compilation, we need to configure the toolchain sdk path on the server, for example: ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", toolchains = { cross = { sdkdir = "~/files/arm-linux-xxx" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` Among them, under toolchains, each item corresponds to a toolchain, here is configured as `cross = {}` cross toolchain, corresponding to `toolchain("cross")`. In the toolchain, we can configure `sdkdir`, `bindir`, `cross`, etc., corresponding to the interface configuration of `set_sdkdir`, `set_bindir` and `set_cross` in `toolchain("cross")`. If cross toolchain comparisonSpecification, we usually only need to configure `sdkdir`, xmake can automatically detect it. And client-side compilation only needs to specify the sdk directory. ```bash $ xmake f -p cross --sdk=/xxx/arm-linux-xxx $ xmake ``` #### Clean server cache The compilation of each project on the server side will generate some cache files, which are stored separately according to the project granularity. We can use the following command to clear the cache corresponding to each server for the current project. ```bash $ xmake service --clean --distcc ``` #### Some optimizations 1. Cache server-side compilation results to avoid repeated compilation 2. Local cache, remote cache optimization, avoid unnecessary server communication 3. Server load balancing scheduling, rational allocation of server resources 4. Small files are compiled directly locally after preprocessing, which is usually faster 5. Real-time compression and transmission of large files, based on lz4 fast compression 6. Internal state maintenance, compared to independent tools such as distcc, avoids frequent independent process loading and time-consuming, and avoids additional communication with the daemon process ### Local compilation cache support By default, Xmake will enable the local cache. The version before 2.6.5 uses the external ccache by default, and after 2.6.6, Xmake provides a built-in cross-platform local cache solution. Compared with third-party independent processes such as ccache, xmake's internal state maintenance is easier to optimize, and it also avoids frequent independent process loading and time-consuming, and avoids additional communication with the daemon process. In addition, the built-in cache can better support cross-platform, and msvc on Windows can also support well, while ccache only supports gcc/clang. Of course, we can also disable the cache with the following command. ```bash $ xmake f --ccache=n ``` Note: Regardless of whether the built-in local cache is used, the configuration name is `--ccache=`, which means the c/c++ build cache, not just the name of the ccache tool. If we want to continue to use other external caching tools, we can also configure it in the following way. ```bash $ xmake f --ccache=n --cxx="ccache gcc" --cc="ccache gcc" $ xmake ``` ### Remote compilation cache support In addition to local caching, we also provide remote caching services, similar to mozilla's sscache, which is usually not used if it is only for personal development. However, if a large-scale project is developed collaboratively by multiple people within the company, distributed compilation and local caching alone are not enough. We also need to cache the compiled object files to a separate server for sharing. In this way, even if other people compile it for the first time, they do not need to compile it distributedly every time, and directly pull the cache from the remote to speed up the compilation. In addition, the remote cache service provided by Xmake is also supported by all platforms, not only gcc/clang but also msvc. #### Start service We can specify the `--ccache` parameter to enable the remote compilation cache service. Of course, if this parameter is not specified, xmake will enable all server-configured services by default. ```console $ xmake service --ccache : listening 0.0.0.0:9092 .. ``` We can also start the service and echo detailed log information. ```console $ xmake service --ccache -vD : listening 0.0.0.0:9092 .. ``` #### Start the service in daemon mode ```console $ xmake service --ccache --start $ xmake service --ccache --restart $ xmake service --ccache --stop ``` #### Configure the server We first, run the `xmake service` command, it will automatically generate a default `server.conf` configuration file, stored in `~/.xmake/service/server.conf`. ```bash $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. an token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9692 .. ``` Then, we edit it, fixing the server's listening port (optional). ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9692", workdir = "/Users/ruki/.xmake/service/server/remote_cache" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` #### Configure the client The client configuration file is in `~/.xmake/service/client.conf`, where we can configure the server address that the client needs to connect to. We can configure multiple server addresses and corresponding tokens in the hosts list. ```console $cat ~/.xmake/service/client.conf { remote_cache = { connect = "127.0.0.1:9692, token = "590234653af52e91b9e438ed860f1a2b" } } } ``` #### Connect to the server After configuring the authentication and server address, you can enter the following command to connect the current project to the configured server. We need to enter `--ccache` when connecting to specify that only the remote compilation cache service is connected. ```bash $ cd projectdir $ xmake service --connect --ccache : connect 127.0.0.1:9692 .. : 127.0.0.1:9692 connected! ``` We can also connect to multiple services at the same time, such as distributed compilation and remote compilation cache services. ```sh $ xmake service --connect --distcc --ccache ``` :::NOTE If there is no parameter, the default connection is the remote compilation service. ::: #### Disconnect server ```bash $ xmake service --disconnect --ccache ``` #### Clean server cache We can also use the following command to clear the cache on the remote server corresponding to the current project. ```bash $ xmake service --clean --ccache ``` And if we execute `xmake clean --all`, when the remote service is connected, all caches will be automatically cleaned up. #### Some optimizations 1. Pull the snapshot of the remote cache and send it back to the local through bloom filter + lz4, which is used to quickly determine whether the cache exists and avoid frequently querying the server cache information 2. With the local cache, you can avoid frequent requests to the remote server and pull the cache. 3. Internal state maintenance, compared with independent tools such as sscache, avoids frequent independent process loading and time-consuming, and avoids additional communication with the daemon process ### Keil/C51 project support We only need to bind to the c51 toolchain, Xmake can automatically detect the Keil/C51 SDK toolchain environment installed on the system, and then use it to compile. ```lua target("hello") add_rules("c51.binary") set_toolchains("c51") add_files("src/main.c") ``` Of course, if you don't set the toolchain through `set_toolchains("c51")`, we can also manually switch to the c51 toolchain through `xmake f --toolchain=c51`. ## Changelog ### New features * [#2327](https://github.com/xmake-io/xmake/issues/2327): Support nvc/nvc++/nvfortran in nvidia-hpc-sdk * Add path instance interfaces * [#2334](https://github.com/xmake-io/xmake/pull/2334): Add lz4 compress module * [#2349](https://github.com/xmake-io/xmake/pull/2349): Add keil/c51 project support * [#274](https://github.com/xmake-io/xmake/issues/274): Distributed compilation support * Use builtin local cache instead of ccache ### Changes * [#2309](https://github.com/xmake-io/xmake/issues/2309): Support user authorization for remote compilation * Improve remote compilation to support lz4 compression ### Bugs fixed * Fix lua stack when select package versions --- --- url: /zh/posts/xmake-update-v2.6.6.md --- ## 新特性介绍 这个版本,我们增加了大量的重量级新特性: * 分布式编译支持 * 内置本地编译缓存 * 远程编译缓存支持 通过这些特性,我们可以更加快速地编译大型 C/C++ 项目。另外,它们完全是跨平台的,不仅支持 gcc/clang 也支持 msvc,而且除了编译器无任何第三方依赖,使用也非常方便。 因此,使用了 Xmake,就等于同时使用了 `distcc/ccache/sccache`。 相比这些第三方的工具,Xmake 完全支持 Windows 和 msvc,在消除了平台差异性的同事,也省去了独立进程调用,以及额外的守护进程带来的开销。 除了这些特性之外,新版本 Xmake 还新增 Keil/c51 项目的编译支持,以及对 nvidia-hpc-sdk 工具链中的 nvc/nvc++/nvfortran 编译器的支持。 ### 远程编译支持用户认证 上个版本我们已经初步支持了远程编译,但是没有提供用户认证支持,这会带来一些安全性问题,因此这个版本,我们新增了用户认证支持。 目前,Xmake 主要提供以下几种认证机制,另外,它对分布式编译和远程缓存也同样生效。 1. Token 认证 2. 密码认证 3. 可信主机验证 #### Token 认证 这也是我们默认推荐的方式,更加安全,配置和连接也更加方便,每次连接也不用输入密码。 我们在执行 `xmake service` 命令时候,默认就会生成一个服务端和客户端的配置文件,并且自动生成一个默认的 token,因此本地直连是不需要任何配置的。 ##### 服务端认证配置 服务端可以配置多个 token 用于对不同用户主机进行授权连接,当然也可以共用一个 token。 ```bash $ cat ~/.xmake/service/server.conf { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f" } } ``` ##### 客户端认证配置 客户端只需要添加服务器上的 token 到对应的客户端配置中即可。 ```bash $ cat ~/.xmake/service/client.conf { remote_build = { connect = "127.0.0.1:9691", token = "e438d816c95958667747c318f1532c0f" } } ``` ##### 手动生成新 token 我们也可以执行下面的命令,手动生成一个新的 token,自己添加到服务器配置中。 ```bash $ xmake service --gen-token New token a7b9fc2d3bfca1472aabc38bb5f5d612 is generated! ``` #### 密码认证 我们也提供密码认证的授权模式,相比 token 认证,它需要用户每次连接的时候,输入密码,验证通过后,才能连接上。 ##### 服务端认证配置 密码认证,我们不需要手动配置 token,只需要执行下面的命令,添加用户就行了,添加过程中,会提示用户输入密码。 ```bash $ xmake service --add-user=ruki Please input user ruki password: 123456 Add user ruki ok! ``` 然后,xmake 就会通过用户名,密码生成一个新的 token 添加到服务器配置的 token 列表中去。 ```bash $ cat ~/.xmake/service/server.conf { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f", "7889e25402413e93fd37395a636bf942" } } ``` 当然,我们也可以删除指定的用户和密码。 ```bash $xmake service --rm-user=ruki Please input user ruki password: 123456 Remove user ruki ok! ``` ##### 客户端认证配置 对于客户端,我们不再需要设置服务器的 token 了,只需要在连接配置中,追加需要连接的用户名即可开启密码认证,格式:`user@address:port` ```bash $ cat ~/.xmake/service/client.conf { remote_build = { connect = "root@127.0.0.1:9691" } } ``` :::注意 如果去掉用户名,也没配置 token,那就是匿名模式,如果服务器也没配置 token,就是完全禁用认证,直接连接。 ::: #### 可信主机验证 另外,为了更进一步提高安全性,我们还提供了服务端可信主机验证,如果在服务器配置的 known\_hosts 列表中,配置了可以连接的客户端主机 ip 地址, 那么只有这些主机可以成功连接上这台服务器,其他主机对它的连接都会被提示为不可信而拒绝连接,即使 token 和密码认证都没问题也不行。 ```bash $ cat ~/.xmake/service/server.conf { logfile = "/Users/ruki/.xmake/service/logs.txt", server = { tokens = { "4b928c7563a0cba10ff4c3f5ca0c8e24" }, known_hosts = { "127.0.0.1", "xx.xx.xx.xx"} } } ``` #### 连接远程的服务器 接下来,我们只需要进入需要远程编译的工程根目录,执行 `xmake service --connect` 命令,进行连接。 如果是 token 认证模式,那么不需要的额外的密码输入,直接连接。 ```console $ xmake create test $ cd test $ xmake service --connect : connect 192.168.56.110:9091 .. : connected! : sync files in 192.168.56.110:9091 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` 如果是密码认证,那么会提示用户输入密码,才能继续连接。 ```bash $ xmake service --connect Please input user root password: 000000 : connect 127.0.0.1:9691 .. : connected! : sync files in 127.0.0.1:9691 .. Scanning files .. Comparing 3 files .. [+]: xmake.lua [+]: .gitignore [+]: src/main.cpp 3 files has been changed! Archiving files .. Uploading files with 1591 bytes .. : sync files ok! ``` 如果密码不对,就会提示错误。 ```bash $ xmake service --connect Please input user root password: 123 : connect 127.0.0.1:9691 .. : connect 127.0.0.1:9691 failed, user and password are incorrect! ``` ### 分布式编译支持 Xmake 提供了内置的分布式编译服务,通常它可以跟 本地编译缓存,远程编译缓存 相互配合,实现最优的编译的加速。 另外,它是完全跨平台支持,我们不仅支持 gcc/clang,也能够很好地支持 Windows 和 msvc。 对于交叉编译,只要交叉工具链支持,我们不要求服务器的系统环境,即使混用 linux, macOS 和 Windows 的服务器资源,也可以很好的实现分布式编译。 #### 开启服务 我们可以指定 `--distcc` 参数来开启分布式编译服务,当然如果不指定这个参数,xmake 会默认开启所有服务端配置的服务。 ```console $ xmake service --distcc : listening 0.0.0.0:9093 .. ``` 我们也可以开启服务的同时,回显详细日志信息。 ```console $ xmake service --distcc -vD : listening 0.0.0.0:9093 .. ``` #### 以 Daemon 模式开启服务 ```console $ xmake service --distcc --start $ xmake service --distcc --restart $ xmake service --distcc --stop ``` #### 配置服务端 我们首先,运行 `xmake service` 命令,它会自动生成一个默认的 `server.conf` 配置文件,存储到 `~/.xmake/service/server.conf`。 ```bash $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. an token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9693 .. ``` 然后,我们编辑它,修复服务器的监听端口(可选)。 ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` #### 配置客户端 客户端配置文件在 `~/.xmake/service/client.conf`,我们可以在里面配置客户端需要连接的服务器地址。 我们可以在 hosts 列表里面配置多个服务器地址,以及对应的 token。 :::注意 分布式编译,推荐使用 token 认证模式,因为密码模式,每台服务器连接时候都要输入一次密码,很繁琐。 ::: ```console $cat ~/.xmake/service/client.conf { distcc_build = { hosts = { { connect = "127.0.0.1:9693", token = "590234653af52e91b9e438ed860f1a2b" } } } } ``` #### 连接服务器 配置完认证和服务器地址后,就可以输入下面的命令,将当前工程连接到配置的服务器上了。 我们需要在连接时候,输入 `--distcc`,指定仅仅连接分布式服务。 ```bash $ cd projectdir $ xmake service --connect --distcc : connect 127.0.0.1:9693 .. : 127.0.0.1:9693 connected! ``` 我们也可以同时连接多个服务,比如分布式编译和远程编译缓存服务。 ```sh $ xmake service --connect --distcc --ccache ``` :::注意 如果不带任何参数,默认连接的是远程编译服务。 ::: #### 分布式编译项目 连接上服务器后,我们就可以像正常本地编译那样,进行分布式编译了,例如: ```bash $ xmake ... [ 93%]: ccache compiling.release src/demo/network/unix_echo_client.c ----> local job [ 93%]: ccache compiling.release src/demo/network/ipv6.c [ 93%]: ccache compiling.release src/demo/network/ping.c [ 93%]: distcc compiling.release src/demo/network/unix_echo_server.c. ----> distcc job [ 93%]: distcc compiling.release src/demo/network/http.c [ 93%]: distcc compiling.release src/demo/network/unixaddr.c [ 93%]: distcc compiling.release src/demo/network/ipv4.c [ 94%]: distcc compiling.release src/demo/network/ipaddr.c [ 94%]: distcc compiling.release src/demo/math/fixed.c [ 94%]: distcc compiling.release src/demo/libm/float.c [ 95%]: ccache compiling.release src/demo/libm/double.c [ 95%]: ccache compiling.release src/demo/other/test.cpp [ 98%]: archiving.release libtbox.a [ 99%]: linking.release demo [100%]: build ok! ``` 其中,带有 distcc 字样的是远程编译任务,其他的都是本地编译任务,默认 xmake 还开启了本地编译缓存,对分布式编译结果进行缓存,避免频繁请求服务器。 另外,我们也可以开启远程编译缓存,跟其他人共享编译缓存,进一步加速多人协同开发的编译。 #### 断开连接 ```bash $ xmake service --disconnect --distcc ``` #### 指定并行编译任务数 我们先简单介绍下,目前根据主机 cpu core 数量默认计算的并行任务数: ```lua local default_njob = math.ceil(ncpu * 3 / 2) ``` 因此,如果不开启分布式编译,默认的最大并行编译任务数就是这个 default\_njob。 如果开启分布式编译后,默认的并行编译任务数就是: ```lua local maxjobs = default_njob + server_count * server_default_njob ``` ##### 修改本地并行任务数 我们只需要通过 `-jN` 就能指定本地并行任务数,但是它不会影响服务端的并行任务数。 ```bash $ xmake -jN ``` ##### 修改服务端并行任务数 如果要修改服务端的并行任务数,需要修改客户端的配置文件。 ```bash $cat ~/.xmake/service/client.conf { distcc_build = { hosts = { { connect = "127.0.0.1:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 8 <------- modify here }, { connect = "192.168.01:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 4 } } } } ``` 可以对每个服务器主机,添加 `njob = N` 参数配置,指定这台服务器可以提供的并行任务数。 #### 分布式编译 Android 项目 xmake 提供的分布式编译服务是完全跨平台的,并且支持 Windows, Linux, macOS, Android, iOS 甚至交叉编译。 如果要进行 Android 项目编译,只需要在服务端配置中,增加 `toolchains` 工具链配置,提供 NDK 的跟路径即可。 ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", toolchains = { ndk = { ndk = "~/files/android-ndk-r21e" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` 然后,我们就可以像正常本地编译那样,分布式编译 Android 项目,甚至可以配置多台 Windows, macOS, Linux 等不同的服务器主机,做为分布式编译服务的资源,来编译它。 只需要下载对应平台的 NDK 就行了。 ```bash $ xmake f -p android --ndk=~/files/xxxx $ xmake ``` #### 分布式编译 iOS 项目 编译 iOS 项目更加简单,因为 Xmake 通常能自动检测到 Xcode,所以只需要像正常本地一样,切一下平台到 ios 即可。 ```bash $ xmake f -p iphoneos $ xmake ``` #### 分布式交叉编译配置 如果要分布式交叉编译,我们需要在服务端配置工具链 sdk 路径,例如: ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", toolchains = { cross = { sdkdir = "~/files/arm-linux-xxx" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` 其中,toolchains 下,每一项对应一个工具链,这里配置为 `cross = {}` 交叉工具链,对应 `toolchain("cross")`。 工具链里面我们可以配置 `sdkdir`, `bindir`, `cross` 等等,对应 `toolchain("cross")` 里面的 `set_sdkdir`, `set_bindir` 和 `set_cross` 等接口配置。 如果交叉工具链比较规范,我们通常只需要配置 `sdkdir`,xmake 就能自动检测到。 而客户端编译也只需要指定 sdk 目录。 ```bash $ xmake f -p cross --sdk=/xxx/arm-linux-xxx $ xmake ``` #### 清理服务器缓存 每个项目在服务端的编译,都会产生一些缓存文件,他们都是按工程粒度分别存储的,我们可以通过下面的命令,对当前工程清理每个服务器对应的缓存。 ```bash $ xmake service --clean --distcc ``` #### 一些内部优化 1. 缓存服务器端编译结果,避免重复编译 2. 本地缓存,远程缓存优化,避免不必要的服务端通信 3. 服务器负载均衡调度,合理分配服务器资源 4. 预处理后小文件直接本地编译,通常会更快 5. 大文件实时压缩传输,基于 lz4 快速压缩 6. 内部状态维护,相比 distcc 等独立工具,避免了频繁的独立进程加载耗时,也避免了与守护进程额外的通信 ### 本地编译缓存支持 默认,Xmake 就会开启本地缓存,2.6.5 之前的版本默认使用外置的 ccache,而 2.6.6 之后版本,Xmake 提供了内置的跨平台本地缓存方案。 相比 ccache 等第三方独立进程,xmake 内部状态维护,更加便于优化,也避免了频繁的独立进程加载耗时,也避免了与守护进程额外的通信。 另外,内置的缓存能够更好的支持跨平台,Windows 上 msvc 也能够很好的支持,而 ccache 仅仅支持 gcc/clang。 当然,我们也可以通过下面的命令禁用缓存。 ```bash $ xmake f --ccache=n ``` 注:不管是否使用内置本地缓存,配置名都是 `--ccache=`,意思是 c/c++ 构建缓存,而不仅仅是指 ccache 工具的名字。 我们如果想继续使用外置的其他缓存工具,我们也是可以通过下面的方式来配置。 ```bash $ xmake f --ccache=n --cxx="ccache gcc" --cc="ccache gcc" $ xmake ``` ### 远程编译缓存支持 除了本地缓存,我们也提供了远程缓存服务,类似 mozilla 的 sscache,如果只是个人开发,平常不会用到它。 但是,如果是公司内部多人协同开发一个大型项目,仅仅靠分布式编译和本地缓存,是不够的。我们还需要对编译的对象文件缓存到独立的服务器上进行共享。 这样,其他人即使首次编译,也不需要每次都去分布式编译它,直接从远程拉取缓存来加速编译。 另外,Xmake 提供的远程缓存服务,也是全平台支持的,不仅支持 gcc/clang 还支持 msvc。 #### 开启服务 我们可以指定 `--ccache` 参数来开启远程编译缓存服务,当然如果不指定这个参数,xmake 会默认开启所有服务端配置的服务。 ```console $ xmake service --ccache : listening 0.0.0.0:9092 .. ``` 我们也可以开启服务的同时,回显详细日志信息。 ```console $ xmake service --ccache -vD : listening 0.0.0.0:9092 .. ``` #### 以 Daemon 模式开启服务 ```console $ xmake service --ccache --start $ xmake service --ccache --restart $ xmake service --ccache --stop ``` #### 配置服务端 我们首先,运行 `xmake service` 命令,它会自动生成一个默认的 `server.conf` 配置文件,存储到 `~/.xmake/service/server.conf`。 ```bash $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. an token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9692 .. ``` 然后,我们编辑它,修复服务器的监听端口(可选)。 ```bash $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9692", workdir = "/Users/ruki/.xmake/service/server/remote_cache" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` #### 配置客户端 客户端配置文件在 `~/.xmake/service/client.conf`,我们可以在里面配置客户端需要连接的服务器地址。 我们可以在 hosts 列表里面配置多个服务器地址,以及对应的 token。 ```console $cat ~/.xmake/service/client.conf { remote_cache = { connect = "127.0.0.1:9692, token = "590234653af52e91b9e438ed860f1a2b" } } } ``` #### 连接服务器 配置完认证和服务器地址后,就可以输入下面的命令,将当前工程连接到配置的服务器上了。 我们需要在连接时候,输入 `--ccache`,指定仅仅连接远程编译缓存服务。 ```bash $ cd projectdir $ xmake service --connect --ccache : connect 127.0.0.1:9692 .. : 127.0.0.1:9692 connected! ``` 我们也可以同时连接多个服务,比如分布式编译和远程编译缓存服务。 ```sh $ xmake service --connect --distcc --ccache ``` :::注意 如果不带任何参数,默认连接的是远程编译服务。 ::: #### 断开连接 ```bash $ xmake service --disconnect --ccache ``` #### 清理服务器缓存 我们也可以通过下面的命令,清理当前工程对应的远程服务器上的缓存。 ```bash $ xmake service --clean --ccache ``` 而如果我们执行 `xmake clean --all`,在连接了远程服务的状态下,也会去自动清理所有的缓存。 #### 一些内部优化 1. 拉取远程缓存的快照,通过 bloom filter + lz4 回传本地后,用于快速判断缓存是否存在,避免频繁的查询服务端缓存信息 2. 配合本地缓存,可以避免频繁地请求远程服务器,拉取缓存。 3. 内部状态维护,相比 sscache 等独立工具,避免了频繁的独立进程加载耗时,也避免了与守护进程额外的通信 ### Keil/C51 工程支持 我们只需要绑定到 c51 工具链,Xmake 就能自动检测到系统安装的 Keil/C51 SDK 工具链环境,然后使用它进行编译。 ```lua target("hello") add_rules("c51.binary") set_toolchains("c51") add_files("src/main.c") ``` 当然,如果不通过 `set_toolchains("c51")` 设置工具链,我们也可以通过 `xmake f --toolchain=c51` 手动切换到 c51 工具链上去。 ## 更新内容 ### 新特性 * [#2327](https://github.com/xmake-io/xmake/issues/2327): 支持 nvidia-hpc-sdk 工具链中的 nvc/nvc++/nvfortran 编译器 * 添加 path 实例接口 * [#2334](https://github.com/xmake-io/xmake/pull/2334): 添加 lz4 压缩模块 * [#2349](https://github.com/xmake-io/xmake/pull/2349): 添加 keil/c51 工程支持 * [#274](https://github.com/xmake-io/xmake/issues/274): 跨平台分布式编译支持 * 使用内置的本地缓存替代 ccache ### 改进 * [#2309](https://github.com/xmake-io/xmake/issues/2309): 远程编译支持用户授权验证 * 改进远程编译,增加对 lz4 压缩支持 ### Bugs 修复 * 修复选择包版本时候 lua 栈不平衡导致的崩溃问题 --- --- url: /posts/xmake-update-v2.7.1.md --- ## Introduction of new features In this release, we have refactored and improved the C++20 Modules implementation, improved the dependency graph parsing of module files, added support for STL and User HeaderUnits, and made the CMakelists/compile\_commands generator support C++ Modules. In addition, we've added an `xmake watch` plugin that can monitor current project file updates in real time, automatically trigger incremental builds, or run some custom commands. ### C++ Modules Improvements Xmake has long supported C++ Modules build support, and can automatically analyze dependencies between modules to maximize parallel compilation. In addition, Xmake uses `.mpp` as the default module extension, but also supports `.ixxx`, `.cppm`, `.mxx` and so on. For example ```lua set_languages("c++20") target("class") set_kind("binary") add_files("src/*.cpp", "src/*.mpp") ``` For more examples see: [C++ Modules](https://github.com/xmake-io/xmake/tree/master/tests/projects/c%2B%2B/modules) However, the previous implementation has many shortcomings. 1. no support for HeaderUnits, so you can't use modules like stl 2. the module dependency graph parsing is done by scanning the source code itself, and does not support the dependency scanning provided by the compiler, so it is not fully reliable 3. do not support CMakelists generation 4. compile\_commands.json generation is not supported In the new version, we have refactored and upgraded the implementation of C++20 module, and we support all the points mentioned above, and added support for Headerunits, so we can introduce STL and user header modules in the module. Also, since higher versions of msvc and gcc have built-in scan analysis of module dependency graphs, Xmake prioritizes module dependency graph analysis with the compiler, and if the compiler does not support it (clang), then Xmake will degrade to its own source code scan implementation. A related patch is available at [#2641](https://github.com/xmake-io/xmake/pull/2641), with many thanks to [@Arthapz](https://github.com/Arthapz) for their contribution. Here is an example of a module that uses STL HeaderUnits, e.g. ```bash stl_headerunit$ xmake [ 0%]: generating.cxx.module.deps src/main.cpp [ 0%]: generating.cxx.module.deps src/hello.mpp [ 20%]: generating.cxx.headerunit.bmi iostream [ 60%]: generating.cxx.module.bmi hello [ 70%]: cache compiling.release src/main.cpp [ 80%]: linking.release stl_headerunit [100%]: build ok! ``` For the first compilation, we scan the module code for dependencies and then precompile stl libraries like iostream as headerunit. Any subsequent recompilation will directly reuse them for compilation acceleration. Note: Usually we need to add at least one `.mpp` file to enable C++20 modules compilation, if there is only a cpp file, module compilation will not be enabled by default. However, if we just want to use the module's Headerunits feature in the cpp file, for example, by introducing some STL Headerunits in the cpp, then we can also set the `.mpp` to `.mpp`. then we can also force C++ Modules compilation by setting `set_policy("build.c++.modules", true)`, e.g. ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") set_languages("c++20") set_policy("build.c++.modules", true) ``` ### Project file monitoring and auto-build In this release, we have added the `xmake watch` plugin command to automatically monitor project files for updates and then trigger automatic builds or run some custom commands. This is often used for personal development to enable fast real-time incremental builds without the need to manually execute the build command each time, improving development efficiency. #### Auto-build after project update The default behavior is to monitor the entire project root, and any file changes will trigger an incremental build of the project. ```bash $ xmake watch watching /private/tmp/test/src/** . watching /private/tmp/test/* ... /private/tmp/test/src/main.cpp modified [ 25%]: ccache compiling.release src/main.cpp [ 50%]: linking.release test [ 100%]: build ok! ``` #### Monitoring a specific directory We can also monitor specific code directories to narrow down the scope of monitoring and improve performance. ```bash $ xmake watch -d src $ xmake watch -d "src;tests/*" ``` The above command will recursively watch all subdirectories. If you want to only watch the files in the current directory without recursive monitoring, you can use the following command. ```bash $ xmake watch -p src $ xmake watch -p "src;tests/*" ``` #### Watch and run the specified command If you want to run the build automatically even after the automatic build, we can use a custom command set. ```bash $ xmake watch -c "xmake; xmake run" ``` The above command list is passed as a string, which is not flexible enough for complex command arguments that require escaping to be more cumbersome, so we can use the following for arbitrary commands. ```bash $ xmake watch -- echo hello xmake! $ xmake watch -- xmake run --help ``` #### Watching and running the target program Although we can automate the running of the target program with custom commands, we also provide more convenient arguments to achieve this behavior. ```bash $ xmake watch -r $ xmake watch --run [100%]: build ok! hello world! ``` #### Watching and running lua scripts We can also watch for file updates and run the specified lua script for more flexible and complex command customization. ```bash $ xmake watch -s /tmp/test.lua ``` We can also get a list of all updated file paths and events in the script again. ```lua function main(events) -- TODO handle events end ``` ### Mac Catalyst Support MAc Catalyst is Apple's new solution for bringing iPad apps to the Mac. Mac apps built with Mac Catalyst share code with your iPad apps, and you can add more features to your Mac separately. With this new version, we've added support for building Mac Catalyst targets, and on macOS platforms, we just need to add the `-appledev=catalyst` configuration option to support compiling existing iOS code and getting it up and running on macOS without making any changes. ```bash $ xmake f --appledev=catalyst $ xmake ``` We can experience the Mac in the test project [iosapp\_with\_framework](https://github.com/xmake-io/xmake/tree/master/tests/projects/objc/iosapp_with_framework) Catalyst program compile and run. ```bash $ xmake [ 36%]: processing.xcode.release src/framework/Info.plist [ 40%]: cache compiling.release src/framework/test.m [ 44%]: linking.release test [ 48%]: generating.xcode.release test.framework [ 56%]: compiling.xcode.release src/app/Assets.xcassets [ 56%]: processing.xcode.release src/app/Info.plist [ 60%]: cache compiling.release src/app/ViewController.m [ 60%]: cache compiling.release src/app/SceneDelegate.m [ 60%]: cache compiling.release src/app/main.m [ 60%]: cache compiling.release src/app/AppDelegate.m [ 60%]: compiling.xcode.release src/app/Base.lproj/LaunchScreen.storyboard [ 60%]: compiling.xcode.release src/app/Base.lproj/Main.storyboard [ 88%]: linking.release demo [ 92%]: generating.xcode.release demo.app [100%]: build ok! $ xmake run 2022-08-26 15:11:03.581 demo[86248:9087199] add(1, 2): 3 2022-08-26 15:11:03.581 demo[86248:9087199] hello xmake! ``` ### Improving remote builds #### Pulling remote build files For remote builds, we have added a new pull remote file command, which can usually be used to download remote target build files, library files locally after the remote build is complete. ```bash $ xmake service --pull 'build/**' outputdir ``` We can match the files to be downloaded with the `-pull 'build/**'` pattern, either build files or other files. Note: Files are segregated by project, only files under the current project can be specified for download, and will not let users download files from other directories on the server to avoid some security risks. #### Real-time echo output In the previous version, when using remote compilation, the client could not output the compilation information of the server in real time, because the cache existed, the compilation progress information seen locally was refreshed piece by piece, which was not a good experience. Therefore, we added line buffer refresh support to improve the real-time output display and make the user experience closer to the local compilation when compiling remotely. ### Improve distributed compile scheduling algorithm We have also further improved the server node scheduling for xmake's distributed compilation by adding weight to cpu load and memory resources, rather than just assigning tasks by the number of cpu cores. Thus, if some nodes are overloaded, we will prioritize the compilation tasks to the nodes that are quite free and take advantage of all compilation resources. ### More flexible cmake package lookup #### Specify links For cmake packages, we have added the `link_libraries` configuration option to allow users to customize the configuration of package dependencies and even support for target links when searching for packages to use with cmake. ```lua add_requires("cmake::xxx", {configs = {link_libraries = {"abc::lib1", "abc::lib2"}}}) ``` xmake automatically appends the following configuration when looking for cmake packages, improving the extraction of links libraries. ```cmake target_link_libraries(test PRIVATE ABC::lib1 ABC::lib2) ``` #### Specify the search mode In addition, we add the following search mode configuration. ```lua add_requires("cmake::xxx", {configs = {search_mode = "config"}}) add_requires("cmake::xxx", {configs = {search_mode = "module"}}) add_requires("cmake::xxx") -- both ``` Specify config search mode, for example, to tell cmake to look for packages from `XXXConfig.cmake`. xmake will automatically append the following configuration internally when it looks for cmake packages. ```cmake find_package(ABC CONFIG REQUIRED) ``` ### armcc/armclang/rc incremental compilation support In the new version, we also perform header dependency analysis for keil's armcc/armclang compiler to support incremental compilation. In addition, msvc's rc.exe resource compiler itself cannot provide header dependency analysis, but cl.exe's preprocessor can handle resource files. Therefore, we can use `cl.exe /E test.rc` to preprocess resource files and extract dependency information from them to achieve incremental compilation support for resource files. So far, it works pretty well, and we also have support for internal ICON/BITMAP resource reference dependencies. ### Other issue fixes We've also made a number of fixes to the build cache, which will be more stable than the previous version. We have also streamlined the generation of CMakelists. More detailed improvements can be found in the following changelog. ## Changelog ### New features * [#2555](https://github.com/xmake-io/xmake/issues/2555): Add fwatcher module and `xmake watch` plugin command * Add `xmake service --pull 'build/**' outputdir` to pull the given files in remote server * [#2641](https://github.com/xmake-io/xmake/pull/2641): Improve C++20 modules, support headerunits and project generators * [#2679](https://github.com/xmake-io/xmake/issues/2679): Support Mac Catalyst ### Changes * [#2576](https://github.com/xmake-io/xmake/issues/2576): More flexible package fetching from cmake * [#2577](https://github.com/xmake-io/xmake/issues/2577): Improve add\_headerfiles(), add `{install = false}` support * [#2603](https://github.com/xmake-io/xmake/issues/2603): Disable `-fdirectives-only` for ccache by default * [#2580](https://github.com/xmake-io/xmake/issues/2580): Set stdout to line buffering * [#2571](https://github.com/xmake-io/xmake/issues/2571): Improve task scheduling for parallel and distributed compilation based on memory/cpu usage * [#2410](https://github.com/xmake-io/xmake/issues/2410): Improve cmakelists generator * [#2690](https://github.com/xmake-io/xmake/issues/2690): Improve to pass toolchains to packages * [#2686](https://github.com/xmake-io/xmake/issues/2686): Support for incremental compilation and parse header file deps for keil/armcc/armclang * [#2562](https://github.com/xmake-io/xmake/issues/2562): Improve include deps for rc.exe * Improve the default parallel building jobs number ### Bugs fixed * [#2614](https://github.com/xmake-io/xmake/issues/2614): Fix building submodules2 tests for msvc * [#2620](https://github.com/xmake-io/xmake/issues/2620): Fix build cache for incremental compilation * [#2177](https://github.com/xmake-io/xmake/issues/2177): Fix python.library segmentation fault for macosx * [#2708](https://github.com/xmake-io/xmake/issues/2708): Fix link error for mode.coverage rule * Fix rpath for macos/iphoneos frameworks and application --- --- url: /zh/posts/xmake-update-v2.7.1.md --- ## 新特性介绍 这个版本我们对 C++20 Modules 的实现进行了重构和改进,改进了模块文件的依赖图解析,新增了对 STL 和 User HeaderUnits 的支持,同时让 CMakelists/compile\_commands 生成器也支持了 C++ Modules。 另外,我们新增了一个 `xmake watch` 插件,可以实时监控当前工程文件更新,自动触发增量构建,或者运行一些自定义的命令。 ### C++ Modules 改进 Xmake 很早就已经支持 C++ Modules 的构建支持,并且能够自动分析模块间的依赖关系,实现最大化的并行编译。 另外,Xmake 采用 `.mpp` 作为默认的模块扩展名,但是也同时支持 `.ixx`, `.cppm`, `.mxx` 等扩展名。 例如: ```lua set_languages("c++20") target("class") set_kind("binary") add_files("src/*.cpp", "src/*.mpp") ``` 更多例子见:[C++ Modules](https://github.com/xmake-io/xmake/tree/master/tests/projects/c%2B%2B/modules) 但是之前的实现还存在很多不足之处: 1. 不支持 HeaderUnits,因此也无法使用 stl 等模块 2. 自己扫描源码实现模块依赖图解析,不支持编译器提供的依赖扫描,因此不完全可靠 3. 不支持 CMakelists 生成 4. 不支持 compile\_commands.json 生成 而在新版中,我们对 C++20 模块的实现进行了重构和升级,上面提到的几点,我们都做了支持,新增了对 Headerunits 的支持,因此我们可以在模块中引入 STL 和 用户头文件模块。 同时,由于 msvc 和 gcc 高版本 都已经内置对模块依赖图的扫描分析,Xmake 会优先借助编译器实现模块依赖图分析,如果编译器不支持(clang),那么 Xmake 也会退化到自己的源码扫描实现上去。 相关的补丁见:[#2641](https://github.com/xmake-io/xmake/pull/2641),非常感谢 [@Arthapz](https://github.com/Arthapz) 的贡献。 下面是一个使用了 STL HeaderUnits 的模块例子,例如: ```bash stl_headerunit$ xmake [ 0%]: generating.cxx.module.deps src/main.cpp [ 0%]: generating.cxx.module.deps src/hello.mpp [ 20%]: generating.cxx.headerunit.bmi iostream [ 60%]: generating.cxx.module.bmi hello [ 70%]: cache compiling.release src/main.cpp [ 80%]: linking.release stl_headerunit [100%]: build ok! ``` 对于首次编译,我们会扫描模块代码之间的依赖关系,然后预编译 iostream 等 stl 库作为 headerunit。 之后的重新编译,都会直接复用它们,实现编译加速。 注:通常我们至少需要添加一个 `.mpp` 文件,才能开启 C++20 modules 编译,如果只有 cpp 文件,默认是不会开启模块编译的。 但是,如果我们仅仅只是想在 cpp 文件中使用模块的 Headerunits 特性,比如引入一些 STL Headerunits 在 cpp 中使用, 那么我们也可以通过设置 `set_policy("build.c++.modules", true)` 来强行开启 C++ Modules 编译,例如: ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") set_languages("c++20") set_policy("build.c++.modules", true) ``` ### 工程文件监视和自动构建 这个版本中,我们新增了 `xmake watch` 插件命令,可以自动监视项目文件更新,然后触发自动构建,或者运行一些自定义命令。 这通常用于个人开发时候,实现快速的实时增量编译,而不需要每次手动执行编译命令,提高开发效率。 #### 项目更新后自动构建 默认行为就是监视整个项目根目录,任何文件改动都会触发项目的增量编译。 ```bash $ xmake watch watching /private/tmp/test/src/** .. watching /private/tmp/test/* .. /private/tmp/test/src/main.cpp modified [ 25%]: ccache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! ``` #### 监视指定目录 我们也可以监视指定的代码目录,缩小监视范围,提升监视性能。 ```bash $ xmake watch -d src $ xmake watch -d "src;tests/*" ``` 上面的命令,会去递归监视所有子目录,如果想要仅仅监视当前目录下的文件,不进行递归监视,可以使用下面的命令。 ```bash $ xmake watch -p src $ xmake watch -p "src;tests/*" ``` #### 监视并运行指定命令 如果想在自动构建后,还想自动运行构建的程序,我们可以使用自定义的命令集。 ```bash $ xmake watch -c "xmake; xmake run" ``` 上面的命令列表是作为字符串传递,这对于复杂命令参数,需要转义比较繁琐不够灵活,那么我们可以使用下面的方式进行任意命令的设置。 ```bash $ xmake watch -- echo hello xmake! $ xmake watch -- xmake run --help ``` #### 监视并运行目标程序 尽管我们可以通过自定义命令来实现目标程序的自动运行,但是我们也提供了更加方便的参数来实现这个行为。 ```bash $ xmake watch -r $ xmake watch --run [100%]: build ok! hello world! ``` #### 监视并运行 lua 脚本 我们还可以监视文件更新后,运行指定的 lua 脚本,实现更加灵活复杂的命令定制。 ```bash $ xmake watch -s /tmp/test.lua ``` 我们还可以再脚本中获取所有更新的文件路径列表和事件。 ```lua function main(events) -- TODO handle events end ``` ### Mac Catalyst 支持 MAc Catalyst 是苹果后来新推的一项让 iPad App 带入 Mac 的方案,通过 Mac Catalyst 构建的 Mac App 与您的 iPad App 共享代码,而且您可以单独为 Mac 添加更多功能。 新版本中,我们新增了 Mac Catalyst 目标的构建支持,在 macOS 平台上,我们只需要添加 `--appledev=catalyst` 配置选项,就可以支持编译现有的 iOS 代码,并让它在 macOS 上运行起来,而无需做任何改动。 ```bash $ xmake f --appledev=catalyst $ xmake ``` 我们可以在 [iosapp\_with\_framework](https://github.com/xmake-io/xmake/tree/master/tests/projects/objc/iosapp_with_framework) 这个测试项目中体验 Mac Catalyst 程序的编译运行。 ```bash $ xmake [ 36%]: processing.xcode.release src/framework/Info.plist [ 40%]: cache compiling.release src/framework/test.m [ 44%]: linking.release test [ 48%]: generating.xcode.release test.framework [ 56%]: compiling.xcode.release src/app/Assets.xcassets [ 56%]: processing.xcode.release src/app/Info.plist [ 60%]: cache compiling.release src/app/ViewController.m [ 60%]: cache compiling.release src/app/SceneDelegate.m [ 60%]: cache compiling.release src/app/main.m [ 60%]: cache compiling.release src/app/AppDelegate.m [ 60%]: compiling.xcode.release src/app/Base.lproj/LaunchScreen.storyboard [ 60%]: compiling.xcode.release src/app/Base.lproj/Main.storyboard [ 88%]: linking.release demo [ 92%]: generating.xcode.release demo.app [100%]: build ok! $ xmake run 2022-08-26 15:11:03.581 demo[86248:9087199] add(1, 2): 3 2022-08-26 15:11:03.581 demo[86248:9087199] hello xmake! ``` ### 改进远程编译 #### 拉取远程构建文件 对于远程编译,我们新增加了一个拉取远程文件的命令,通常可用于远程编译完成后,下载远程的目标生成文件,库文件到本地。 ```bash $ xmake service --pull 'build/**' outputdir ``` 我们可以通过 `--pull 'build/**'` 模式匹配需要下载的文件,可以是构建文件,也可以是其他文件。 注:文件是按项目隔离的,只能指定下载当前项目下的文件,并不会让用户下载服务器上其他目录下的文件,避免一些安全隐患。 #### 实时回显输出 先前的版本在使用远程编译的时候,客户端是无法实时输出服务端的编译信息的,由于缓存的存在,本地看到的编译进度信息都是一块一块刷新出来,体验不是很好。 因此我们加上了行缓冲刷新支持,提高了输出回显的实时性,使得用户在远程编译时,更接近本地编译的体验。 ### 改进分布式编译调度算法 我们对 xmake 的分布式编译的服务器节点调度也做了进一步改进,加上了 cpu 负载和内存资源的权重,而不仅仅通过 cpu core 数量来分配任务。 因此,如果某些节点负载过高,我们会优先将编译任务调度到相当比较空闲的节点上去,充分利用所有编译资源。 ### 更灵活的 cmake 包查找 #### 指定链接 对于 cmake 包,我们新增了 `link_libraries` 配置选项,让用户在查找使用 cmake 包的时候,可以自定义配置包依赖的链接库,甚至对 target 链接的支持。 ```lua add_requires("cmake::xxx", {configs = {link_libraries = {"abc::lib1", "abc::lib2"}}}) ``` xmake 在查找 cmake 包的时候,会自动追加下面的配置,改进对 links 库的提取。 ```cmake target_link_libraries(test PRIVATE ABC::lib1 ABC::lib2) ``` #### 指定搜索模式 另外,我们增加的搜索模式配置: ```lua add_requires("cmake::xxx", {configs = {search_mode = "config"}}) add_requires("cmake::xxx", {configs = {search_mode = "module"}}) add_requires("cmake::xxx") -- both ``` 比如指定 config 搜索模式,告诉 cmake 从 `XXXConfig.cmake` 中查找包。 xmake 在查找 cmake 包的时候,内部会自动追加下面的配置。 ```cmake find_package(ABC CONFIG REQUIRED) ``` ### armcc/armclang/rc 增量编译支持 在新版本中,我们对 keil 的 armcc/armclang 编译器也进行头文件依赖分析,来支持增量编译。 另外,msvc 的 rc.exe 资源编译器本身是无法提供头文件依赖分析的,但是 cl.exe 的预处理器却是可以处理资源文件的。 因此我们可以通过 `cl.exe /E test.rc` 去预处理资源文件,从中提取依赖信息,来实现资源文件的增量编译支持。 目前测试下来,效果还不错,同时我们也对内部 ICON/BITMAP 的资源引用依赖也做了支持。 ### 其他问题修复 我们对构建缓存也做了很多修复,它将比之前的版本更加的稳定。另外我们也精简了 CMakelists 的生成。 更多细节改进见下面的更新日志: ## 更新内容 ### 新特性 * [#2555](https://github.com/xmake-io/xmake/issues/2555): 添加 fwatcher 模块和 `xmake watch` 插件命令 * 添加 `xmake service --pull 'build/**' outputdir` 命令去拉取远程构建服务器上的文件 * [#2641](https://github.com/xmake-io/xmake/pull/2641): 改进 C++20 模块, 支持 headerunits 和 project 生成 * [#2679](https://github.com/xmake-io/xmake/issues/2679): 支持 Mac Catalyst 构建 ### 改进 * [#2576](https://github.com/xmake-io/xmake/issues/2576): 改进从 cmake 中查找包,提供更过灵活的可选配置 * [#2577](https://github.com/xmake-io/xmake/issues/2577): 改进 add\_headerfiles(),增加 `{install = false}` 支持 * [#2603](https://github.com/xmake-io/xmake/issues/2603): 为 ccache 默认禁用 `-fdirectives-only` * [#2580](https://github.com/xmake-io/xmake/issues/2580): 设置 stdout 到 line 缓冲输出 * [#2571](https://github.com/xmake-io/xmake/issues/2571): 改进分布式编译的调度算法,增加 cpu/memory 状态权重 * [#2410](https://github.com/xmake-io/xmake/issues/2410): 改进 cmakelists 生成 * [#2690](https://github.com/xmake-io/xmake/issues/2690): 改机传递 toolchains 到包 * [#2686](https://github.com/xmake-io/xmake/issues/2686): 改进 armcc/armclang 支持增量编译 * [#2562](https://github.com/xmake-io/xmake/issues/2562): 改进 rc.exe 对引用文件依赖的解析和增量编译支持 * 改进默认的并行构建任务数 ### Bugs 修复 * [#2614](https://github.com/xmake-io/xmake/issues/2614): 为 msvc 修复构建 submodules2 测试工程 * [#2620](https://github.com/xmake-io/xmake/issues/2620): 修复构建缓存导致的增量编译问题 * [#2177](https://github.com/xmake-io/xmake/issues/2177): 修复 python.library 在 macOS 上段错误崩溃 * [#2708](https://github.com/xmake-io/xmake/issues/2708): 修复 mode.coverage 规则的链接错误 * 修复 ios/macOS framework 和 application 的 rpath 加载路径 --- --- url: /posts/xmake-update-v2.7.2.md --- ## Introduction of new features ### Building third party libraries more intelligently In previous versions, Xmake provided a TryBuild mode that allowed you to use Xmake to try to build third-party projects maintained by autoconf/cmake/meson etc. directly without xmake.lua. In effect, this means that Xmake detects the corresponding build system and invokes commands such as cmake to do so, but it will help the user to simplify the configuration operation, plus it will interface with xmake's cross-compilation toolchain configuration. However, this mode has a certain failure rate, which can lead to build failure if, for example 1. the project code itself is flawed, resulting in a compilation error 2. the project code does not support the current platform 3. the build script is flawed 4. specific configuration parameters are missing 5. a missing dependency library that needs to be installed by the user 6. the compiler version is too low and does not support some of the code The TryBuild mode usually handles these cases, but in this new version we have introduced a new mechanism to the TryBuild mode to improve the build logic by reusing build scripts from the [xmake-repo](https://github.com/xmake-io/xmake-repo) repository. It roughly handles the process in the following way. 1. execute the xmake command in the third-party source repository directory 2. xmake gets the directory name and tries to resolve the project name and version 3. try to match an existing package from the xmake-repo repository 4. If the match is successful, build directly using the build logic in the package 5. if no match is made, fall back to the original TryBuild logic What is the benefit of this, if the match is successful, we can solve all the problems mentioned above. Even if the current project source code does not support a given platform, or if the source code and build script are flawed in some way, Xmake will automatically patch in a specific patch to fix it and bring in the required dependencies to ensure that it will definitely compile in one click. Let's take a look at the libjpeg library as an example. #### The first step is to download the corresponding source code package ```bash $ wget https://jaist.dl.sourceforge.net/project/libjpeg-turbo/2.1.4/libjpeg-turbo-2.1.4.tar.gz $ tar -xvf libjpeg-turbo-2.1.4.tar.gz $ cd libjpeg-turbo-2.1.4 ``` #### Enter the directory and execute the Xmake command Xmake will prompt the user if it detects that it is the libjpeg library, and whether to build it as libjpeg 2.1.4. ```bash ruki-2:libjpeg-turbo-2.1.4 ruki$ xmake note: libjpeg-turbo 2.1.4 in xmake-repo found, try building it or you can run ``xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) ``` We hit enter to confirm to continue the build. ```bash checking for cmake ... /usr/local/bin/cmake /usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_SHARED=OFF -DENABLE_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_ INSTALL_LIBDIR:PATH=lib -DCMAKE_INSTALL_PREFIX=/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2 -G "Unix Makefiles" -DCMAKE_POSITION_INDEPENDENT_CODE=ON /Users/ruki/Downloads/libjpeg-turbo-2.1.4 -- CMAKE_BUILD_TYPE = Release -- VERSION = 2.1.4, BUILD = 20220923 -- 64-bit build (x86_64) -- CMAKE_INSTALL_PREFIX = /Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2 -- CMAKE_INSTALL_BINDIR = bin (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/bin) -- CMAKE_INSTALL_DATAROOTDIR = share (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share) -- CMAKE_INSTALL_DOCDIR = share/doc/libjpeg-turbo (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/ share/doc/libjpeg-turbo) -- CMAKE_INSTALL_INCLUDEDIR = include (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/include) -- CMAKE_INSTALL_LIBDIR = lib (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/lib) -- CMAKE_INSTALL_MANDIR = share/man (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share/man) -- Shared libraries disabled (ENABLE_SHARED = 0) -- Static libraries enabled (ENABLE_STATIC = 1) -- 12-bit JPEG support disabled (WITH_12BIT = 0) -- Arithmetic decoding support enabled (WITH_ARITH_DEC = 1) -- Arithmetic encoding support enabled (WITH_ARITH_ENC = 1) -- TurboJPEG API library enabled (WITH_TURBOJPEG = 1) -- TurboJPEG Java wrapper disabled (WITH_JAVA = 0) -- In-memory source/destination managers enabled (WITH_MEM_SRCDST = 1) -- Emulating libjpeg API/ABI v6.2 (WITH_JPEG7 = 0, WITH_JPEG8 = 0) -- libjpeg API shared library version = 62.3.0 -- Compiler flags = -O3 -DNDEBUG -- Linker flags = -- INLINE = __inline__ __attribute__((always_inline)) (FORCE_INLINE = 1) -- THREAD_LOCAL = __thread -- CMAKE_EXECUTABLE_SUFFIX = -- CMAKE_ASM_NASM_COMPILER = /usr/local/bin/nasm -- CMAKE_ASM_NASM_OBJECT_FORMAT = macho64 -- CMAKE_ASM_NASM_FLAGS = -DMACHO -D__x86_64__ -DPIC -- SIMD extensions: x86_64 (WITH_SIMD = 1) -- FLOATTEST = sse -- Configuring done -- Generating done -- Build files have been written to: /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build_646b7957 make -j10 [ 2%] Built target md5cmp [ 19%] Built target wrjpgcom [ 20%] Built target simd [ 21%] Built target strtest [ 22%] Built target rdjpgcom [ 80%] Built target jpeg-static [ 84%] Built target turbojpeg-static [ 90%] Built target tjbench-static [ 90%] Built target tjunittest-static [ 91%] Built target jpegtran-static [ 98%] Built target djpeg-static [ 100%] Built target cjpeg-static make install [ 1%] Built target strtest [ 3%] Built target wrjpgcom [ 19%] Built target simd [ 52%] Built target turbojpeg-static [ 53%] Built target rdjpgcom [ 82%] Built target jpeg-static [ 85%] Built target jpegtran-static [ 90%] Built target djpeg-static [ 93%] Built target tjunittest-static [ 97%] Built target cjpeg-static [ 98%] Built target tjbench-static [100%] Built target md5cmp Install the project... exporting libjpeg-turbo-2.1.4 -> /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2 output to /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts build ok! ``` As long as the match is detected, the build will usually complete with a near 100% success rate, and Xmake will output the build product to the current directory under `build/artifacts`. #### Interfacing with cross-compilation toolchains This smart build mode allows us to not only build native applications, but also to interface with the cross-compilation toolchain to support ios/android and any cross-compilation platform. For example, to build on Android, we simply pass the `--trybuild=xrepo` argument and switch to Android, and Xmake will pass all the ndk toolchain information. \`\`bash $ xmake f -p android --trybuild=xrepo --ndk=~/files/android-ndk-r20b -c $ xmake xmake f -c --require=n -v -p android -a armeabi-v7a -m release -k static --ndk=/Users/ruki/files/android-ndk-r20b checking for Android SDK directory ... ~/Library/Android/sdk checking for Build Tools Version of Android SDK ... 33.0.0 checking for NDK directory ... /Users/ruki/files/android-ndk-r20b checking for SDK version of NDK ... ... 21 checking for clang++ ... /Users/ruki/files/android-ndk-r20b/toolchains/llvm/prebuilt/darwin-x86\_64/bin/clang++ checking for the shared library linker (sh) ... clang++ checking for clang++ ... /Users/ruki/files/android-ndk-r20b/toolchains/llvm/prebuilt/darwin-x86\_64/bin/clang++ checking for the linker (ld) ... clang++ ... exporting libjpeg-turbo-2.1.4 -> /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts/l/libjpeg-turbo/2.1.4/79c2e21f436b4ab08a3c23a6cbae8c0e output to /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts build ok! ```` #### fallback to direct compilation If we don't want to use the xmake-repo build scripts, we can fall back to cmake/autoconf and try to build them directly. However, this may have a certain failure rate and may compile additional binary targets that are not needed. The build script in xmake-repo is optimised to streamline a lot of unnecessary build parameters, such as disabling the tests/examples build. We just need to hit n to cancel the smart build mode based on package scripts, and Xmake will give a new prompt to let the user choose whether to continue with the cmake/autoconf build attempt. ```bash $ xmake note: libjpeg-turbo 2.1.4 in xmake-repo found, try building it or you can run ``xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) n note: CMakeLists.txt found, try building it or you can run `xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) ```` ### Support for Windows Arm64 We have also improved our Windows build support with the addition of Windows Arm64 platform support, simply by switching to the arm64 architecture. ```bash $ xmake f -a arm64 $ xmake ``` ### Improved rule support for sequential execution of dependencies Associated dependencies can be bound to a batch of rules, i.e. instead of having to add rules to target one by one using `add_rules()`, just apply a rule that will take effect for it and all its dependencies. For example ```lua rule("foo") add_deps("bar") rule("bar") ... ``` We only need `add_rules("foo")` to apply both foo and bar rules. However, by default there is no order of execution between dependencies, and scripts such as `on_build_file` for foo and bar are executed in parallel, in an undefined order. To tightly control the order of execution, in newer versions we can configure `add_deps("bar", {order = true})` to tell xmake that we need to execute scripts at the same level according to the order of dependencies. Example. ```lua rule("foo") add_deps("bar", {order = true}) on_build_file(function (target, sourcefile) end) rule("bar") on_build_file(function (target, sourcefile) end) ``` bar's `on_build_file` will be executed first. ### Better dynamic configuration of targets and rules The above way of controlling rule dependencies only works if both foo and bar rules are custom rules, which doesn't work if you want to insert your own rules to be executed before xmake's built-in rules. In this case, we need to use a more flexible dynamic rule creation and injection approach to modify the built-in rules. For example, if we want to execute the `on_build_file` script for a custom cppfront rule before the built-in `c++.build` rule, we can do this in the following way. ```lua rule("cppfront") set_extensions(".cpp2") on_load(function (target) local rule = target:rule("c++.build"):clone() rule:add("deps", "cppfront", {order = true}) target:rule_add(rule) end) on_build_file(function (target, sourcefile, opt) print("build cppfront file") end) target("test") set_kind("binary") add_rules("cppfront") add_files("src/*.cpp") add_files("src/*.cpp2") ``` ### Support for introducing custom rules from packages Now, we can also add custom build rule scripts to the package management repository to enable dynamic distribution and installation following the package. We need to put the custom rules into the `packages/x/xxx/rules` directory of the repository and it will follow the package as it is installed. It does, of course, have some limitations. * In package rules, we cannot add `on_load`, `after_load` scripts, but we can usually use `on_config` instead. #### Adding package rules We need to add the rules script to the rules fixed directory, for example: packages/z/zlib/rules/foo.lua ```lua rule("foo") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` #### Applying package rules The rules are used in a similar way as before, the only difference being that we need to specify which package's rules to access by prefixing them with `@packagename/`. The exact format: ``add_rules("@packagename/rulename")`, for example: ``add\_rules("@zlib/foo")\`. \`\`lua add\_requires("zlib", {system = false}) target("test") set\_kind("binary") add\_files("src/\*.cpp") add\_packages("zlib") add\_rules("@zlib/foo") ```` #### Referencing rules by package alias If a package alias exists, xmake will give preference to the package alias to get the rules. ``` lua add_requires("zlib", {alias = "zlib2", system = false}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib2") add_rules("@zlib2/foo") ```` #### Adding package rule dependencies We can use `add_deps("@bar")` to add additional rules relative to the current package directory. However, we cannot add rule dependencies from other packages, they are completely isolated and we can only refer to rules from other packages imported by `add_requires` in the user project. packages/z/zlib/rules/foo.lua ```lua rule("foo") add_deps("@bar") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` packages/z/zlib/rules/bar.lua ```lua rule("bar") on_config(function (target) print("bar: on_config %s", target:name()) end) ``` ### Stricter package dependency compatibility support Two new package related policies have been added to enable stricter package dependency compatibility control. This is to address the fact that some packages may have abi incompatibilities or break other packages that depend on them every time they are updated, and by default Xmake will not recompile and install them unless their versions and configurations are also updated. There is a chance that the compilation compatibility will be broken and the link will fail. #### package.librarydeps.strict\_compatibility is disabled by default, if enabled then strict compatibility is maintained between the current package and all its library dependencies, and any version update of a dependent package will force a recompile install of the current package. This ensures that all packages are binary compatible and that no linking and runtime errors occur when linking with other installed packages due to changes to the interface of a dependent package. ```lua package("foo") add_deps("bar", "zoo") set_policy("package.librarydeps.strict_compatibility", true) ``` For example, if there is an updated version of bar or zoo, then foo will also be recompiled and installed. #### package.strict\_compatibility is disabled by default, if it is enabled then strict compatibility is maintained between the current package and all other packages that depend on it, and any version update of this package will force a recompile and install of the other parent packages. This ensures that all packages are binary compatible and that no linking and runtime errors occur when linking with other installed packages due to changes in the interface of a dependent package. ```lua package("foo") set_policy("package.strict_compatibility", true) package("bar") add_deps("foo") package("zoo") add_deps("foo") ``` For example, if there is an updated version of foo, then both bar and zoo will be forced to recompile and install. #### package.install\_always This is useful for local integration of third-party source packages, as the package will always be reinstalled each time `xmake f -c` is run to reconfigure it. As the user may at any time need to modify the third party source code and recompile it for integration. Previously it was only possible to trigger a recompile by changing the package version number each time, but with this strategy it is possible to trigger a recompile each time. ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) set_policy("package.install_always", true) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" . (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" ... (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` ### Adding the clang-cl toolchain Although we did support switching to the clang-cl compiler in previous versions, the switch was cumbersome and had to be set up one by one. ```bash $ xmake f --cxx=clang-cl --cc=clang-cl -c $ xmake ``` And you have to add the directory where clang-cl.exe is located to %PATH% to make it work. Now that vs comes with the clang-cl toolchain, Xmake is fully capable of detecting it and using it automatically. So, in this new version, we have added the clang-cl toolchain, and all it takes is `xmake f --toolchain=clang-cl` to quickly switch to the clang-cl toolchain without any PATH settings. ## Changelog ### New features * [#2140](https://github.com/xmake-io/xmake/issues/2140): Support Windows Arm64 * [#2719](https://github.com/xmake-io/xmake/issues/2719): Add `package.librarydeps.strict_compatibility` to strict compatibility for package linkdeps * [#2810](https://github.com/xmake-io/xmake/pull/2810): Support os.execv to run shell script file * [#2817](https://github.com/xmake-io/xmake/pull/2817): Improve rule to support dependence order * [#2824](https://github.com/xmake-io/xmake/pull/2824): Pass cross-file to meson.install and trybuild * [#2856](https://github.com/xmake-io/xmake/pull/2856): Improve to debug package using the debug source directory * [#2859](https://github.com/xmake-io/xmake/issues/2859): Improve trybuild to build 3rd source library using xmake-repo scripts * [#2879](https://github.com/xmake-io/xmake/issues/2879): Support for dynamic creation and injection of rules and targets in script scope * [#2374](https://github.com/xmake-io/xmake/issues/2374): Allow xmake package to embed rules and scripts * Add clang-cl toolchain ### Changes * [#2745](https://github.com/xmake-io/xmake/pull/2745): Improve os.cp to support symlink * [#2773](https://github.com/xmake-io/xmake/pull/2773): Improve vcpkg packages to support freebsd * [#2778](https://github.com/xmake-io/xmake/pull/2778): Improve Improve xrepo.env for target * [#2783](https://github.com/xmake-io/xmake/issues/2783): Add digest algorithm option for wdk signtool * [#2787](https://github.com/xmake-io/xmake/pull/2787): Improve json to support empty array * [#2782](https://github.com/xmake-io/xmake/pull/2782): Improve to find matlab and runtime * [#2793](https://github.com/xmake-io/xmake/issues/2793): Improve mconfdialog * [#2804](https://github.com/xmake-io/xmake/issues/2804): Support macOS arm64/x86\_64 cross-compilation for installing packages * [#2809](https://github.com/xmake-io/xmake/issues/2809): Improve cl optimization option * Improve trybuild for meson/cmake/autoconf * [#2846](https://github.com/xmake-io/xmake/discussions/2846): Improve to generate config files * [#2866](https://github.com/xmake-io/xmake/issues/2866): Better control over the order of execution of rules ### Bugs fixed * [#2740](https://github.com/xmake-io/xmake/issues/2740): Fix build c++ modules stuck and slower for msvc * [#2875](https://github.com/xmake-io/xmake/issues/2875): Fix build linux driver error * [#2885](https://github.com/xmake-io/xmake/issues/2885): Fix pch not found with msvc/ccache --- --- url: /zh/posts/xmake-update-v2.7.2.md --- ## 新特性介绍 ### 更加智能化构建第三方库 在先前的版本中,Xmake 提供了一种 TryBuild 模式,可以在没有 xmake.lua 的情况下,使用 Xmake 尝试对 autoconf/cmake/meson 等维护的第三方项目进行直接构建。 其实,也就是让 Xmake 检测到对应的构建系统后,调用 cmake 等命令来实现,但是会帮助用户简化配置操作,另外还能对接 xmake 的交叉编译工具链配置。 但是,这种模式有一定的失败率,比如以下一些情况,都会可能导致构建失败: 1. 项目代码自身存在缺陷,导致编译错误 2. 项目代码不支持当前平台 3. 构建脚本存在缺陷 4. 缺少特定的配置参数 5. 缺少依赖库,需要用户手动安装 6. 编译器版本太低,不支持部分代码 而 TryBuild 模式通常处理这些情况,但是在新版本中,我们对 TryBuild 模式引入了一种新的机制,通过复用 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库中的构建脚本,来改进构建逻辑。 它大概得处理流程是这样子的: 1. 在第三方源码库目录执行 xmake 命令 2. Xmake 获取目录名,尝试解析项目名和版本 3. 尝试从 xmake-repo 仓库匹配现有的包 4. 如果匹配成功,直接采用包中构建逻辑来构建 5. 如果没匹配成功,回退到原来的 TryBuild 逻辑 这能带来什么好处呢,如果匹配成功,我们能够解决上面提到的各种问题。 即使当前项目源码不支持指定平台,或者源码和构建脚本存在一定的缺陷,Xmake 也能自动打入特定 patch 去修复它,并引入需要的依赖包,确保它肯定能够一键编译通过。 我们使用 libjpeg 库为例,来直观的感受下。 #### 首先是下载对应源码包 ```bash $ wget https://jaist.dl.sourceforge.net/project/libjpeg-turbo/2.1.4/libjpeg-turbo-2.1.4.tar.gz $ tar -xvf libjpeg-turbo-2.1.4.tar.gz $ cd libjpeg-turbo-2.1.4 ``` #### 然后进入目录执行 Xmake 命令 Xmake 如果检测到是 libjpeg 库,就会提示用户,是否作为 libjpeg 2.1.4 来构建。 ```bash ruki-2:libjpeg-turbo-2.1.4 ruki$ xmake note: libjpeg-turbo 2.1.4 in xmake-repo found, try building it or you can run `xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) ``` 我们按下回车键确认继续构建。 ```bash checking for cmake ... /usr/local/bin/cmake /usr/local/bin/cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_SHARED=OFF -DENABLE_STATIC=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_INSTALL_LIBDIR:PATH=lib -DCMAKE_INSTALL_PREFIX=/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2 -G "Unix Makefiles" -DCMAKE_POSITION_INDEPENDENT_CODE=ON /Users/ruki/Downloads/libjpeg-turbo-2.1.4 -- CMAKE_BUILD_TYPE = Release -- VERSION = 2.1.4, BUILD = 20220923 -- 64-bit build (x86_64) -- CMAKE_INSTALL_PREFIX = /Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2 -- CMAKE_INSTALL_BINDIR = bin (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/bin) -- CMAKE_INSTALL_DATAROOTDIR = share (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share) -- CMAKE_INSTALL_DOCDIR = share/doc/libjpeg-turbo (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share/doc/libjpeg-turbo) -- CMAKE_INSTALL_INCLUDEDIR = include (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/include) -- CMAKE_INSTALL_LIBDIR = lib (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/lib) -- CMAKE_INSTALL_MANDIR = share/man (/Users/ruki/.xmake/packages/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2/share/man) -- Shared libraries disabled (ENABLE_SHARED = 0) -- Static libraries enabled (ENABLE_STATIC = 1) -- 12-bit JPEG support disabled (WITH_12BIT = 0) -- Arithmetic decoding support enabled (WITH_ARITH_DEC = 1) -- Arithmetic encoding support enabled (WITH_ARITH_ENC = 1) -- TurboJPEG API library enabled (WITH_TURBOJPEG = 1) -- TurboJPEG Java wrapper disabled (WITH_JAVA = 0) -- In-memory source/destination managers enabled (WITH_MEM_SRCDST = 1) -- Emulating libjpeg API/ABI v6.2 (WITH_JPEG7 = 0, WITH_JPEG8 = 0) -- libjpeg API shared library version = 62.3.0 -- Compiler flags = -O3 -DNDEBUG -- Linker flags = -- INLINE = __inline__ __attribute__((always_inline)) (FORCE_INLINE = 1) -- THREAD_LOCAL = __thread -- CMAKE_EXECUTABLE_SUFFIX = -- CMAKE_ASM_NASM_COMPILER = /usr/local/bin/nasm -- CMAKE_ASM_NASM_OBJECT_FORMAT = macho64 -- CMAKE_ASM_NASM_FLAGS = -DMACHO -D__x86_64__ -DPIC -- SIMD extensions: x86_64 (WITH_SIMD = 1) -- FLOATTEST = sse -- Configuring done -- Generating done -- Build files have been written to: /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build_646b7957 make -j10 [ 2%] Built target md5cmp [ 19%] Built target wrjpgcom [ 20%] Built target simd [ 21%] Built target strtest [ 22%] Built target rdjpgcom [ 80%] Built target jpeg-static [ 84%] Built target turbojpeg-static [ 90%] Built target tjbench-static [ 90%] Built target tjunittest-static [ 91%] Built target jpegtran-static [ 98%] Built target djpeg-static [100%] Built target cjpeg-static make install [ 1%] Built target strtest [ 3%] Built target wrjpgcom [ 19%] Built target simd [ 52%] Built target turbojpeg-static [ 53%] Built target rdjpgcom [ 82%] Built target jpeg-static [ 85%] Built target jpegtran-static [ 90%] Built target djpeg-static [ 93%] Built target tjunittest-static [ 97%] Built target cjpeg-static [ 98%] Built target tjbench-static [100%] Built target md5cmp Install the project... exporting libjpeg-turbo-2.1.4 -> /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts/l/libjpeg-turbo/2.1.4/646b795702e34be89c5745333d052aa2 output to /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts build ok! ``` 只要检测匹配成功,通常肯定能够完成编译,成功率接近 100%,最后 Xmake 会将编译产物输出到当前目录的 `build/artifacts` 下面。 #### 对接交叉编译工具链 这种智能构建模式,我们不仅能够编译本机程序,还可以对接交叉编译工具链,实现对 ios/android 以及任意交叉编译平台的支持。 例如,编译 Android 平台,我们只需要传递 `--trybuild=xrepo` 参数,然后切换到 android 平台即可,Xmake 会透传所有 ndk 工具链信息。 ```bash $ xmake f -p android --trybuild=xrepo --ndk=~/files/android-ndk-r20b -c $ xmake xmake f -c --require=n -v -p android -a armeabi-v7a -m release -k static --ndk=/Users/ruki/files/android-ndk-r20b checking for Android SDK directory ... ~/Library/Android/sdk checking for Build Tools Version of Android SDK ... 33.0.0 checking for NDK directory ... /Users/ruki/files/android-ndk-r20b checking for SDK version of NDK ... 21 checking for clang++ ... /Users/ruki/files/android-ndk-r20b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ checking for the shared library linker (sh) ... clang++ checking for clang++ ... /Users/ruki/files/android-ndk-r20b/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ checking for the linker (ld) ... clang++ ... exporting libjpeg-turbo-2.1.4 -> /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts/l/libjpeg-turbo/2.1.4/79c2e21f436b4ab08a3c23a6cbae8c0e output to /Users/ruki/Downloads/libjpeg-turbo-2.1.4/build/artifacts build ok! ``` #### 回退到直接编译 如果我们不想使用 xmake-repo 的构建脚本,我们也能回退到 cmake/autoconf 直接去尝试构建它们。 但是这样可能会存在一定的失败率,并且有可能会额外编译一些不需要的二进制目标。而 xmake-repo 里面的构建脚本是最优化的,精简了很多没必要的构建参数,比如禁用 tests/examples 构建等等。 我们只需要先敲 n 取消基于包脚本的智能构建模式,Xmake 会有新的提示,让用户选择是否继续采用 cmake/autoconf 来尝试构建。 ```bash $ xmake note: libjpeg-turbo 2.1.4 in xmake-repo found, try building it or you can run `xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) n note: CMakeLists.txt found, try building it or you can run `xmake f --trybuild=` to set buildsystem (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) ``` ### 支持 Windows Arm64 新版本我们还对 Windows 的构建支持做了改进,新增了 Windows Arm64 平台支持,只需要切换到 arm64 架构即可。 ```bash $ xmake f -a arm64 $ xmake ``` ### 改进规则支持依赖顺序执行 关联依赖可以绑定一批规则,也就是不必对 target 挨个去使用 `add_rules()` 添加规则,只需要应用一个规则,就能生效它和它的所有依赖规则。 例如: ```lua rule("foo") add_deps("bar") rule("bar") ... ``` 我们只需要 `add_rules("foo")`,就能同时应用 foo 和 bar 两个规则。 但是,默认情况下,依赖之间是不存在执行的先后顺序的,foo 和 bar 的 `on_build_file` 等脚本是并行执行的,顺序未定义。 如果要严格控制执行顺序,在新版本中,我们可以配置 `add_deps("bar", {order = true})`,告诉 xmake,我们需要根据依赖顺序来执行同级别的脚本。 例如: ```lua rule("foo") add_deps("bar", {order = true}) on_build_file(function (target, sourcefile) end) rule("bar") on_build_file(function (target, sourcefile) end) ``` bar 的 `on_build_file` 将会被先执行。 ### 更好的动态配置目标和规则 上面这种控制规则依赖的方式,只适合 foo 和 bar 两个规则都是自定义规则,如果想要将自己的规则插入到 xmake 的内置规则之前执行,这就不适用了。 这个时候,我们需要使用更加灵活的动态规则创建和注入的方式,去修改内置规则。 例如,我们想在内置的 `c++.build` 规则之前,执行自定义 cppfront 规则的 `on_build_file` 脚本,我们可以通过下面的方式来实现。 ```lua rule("cppfront") set_extensions(".cpp2") on_load(function (target) local rule = target:rule("c++.build"):clone() rule:add("deps", "cppfront", {order = true}) target:rule_add(rule) end) on_build_file(function (target, sourcefile, opt) print("build cppfront file") end) target("test") set_kind("binary") add_rules("cppfront") add_files("src/*.cpp") add_files("src/*.cpp2") ``` ### 支持从包中引入自定义规则 现在,我们还可以在包管理仓库中,添加自定义构架规则脚本,实现跟随包进行动态下发和安装。 我们需要将自定义规则放到仓库的 `packages/x/xxx/rules` 目录中,它会跟随包一起被安装。 当然,它也存在一些限制: * 在包中规则,我们不能添加 `on_load`, `after_load` 脚本,但是通常我们可以使用 `on_config` 来代替。 #### 添加包规则 我们需要将规则脚本添加到 rules 固定目录下,例如:packages/z/zlib/rules/foo.lua ```lua rule("foo") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` #### 应用包规则 使用规则的方式跟之前类似,唯一的区别就是,我们需要通过 `@packagename/` 前缀去指定访问哪个包里面的规则。 具体格式:`add_rules("@packagename/rulename")`,例如:`add_rules("@zlib/foo")`。 ```lua add_requires("zlib", {system = false}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib") add_rules("@zlib/foo") ``` #### 通过包别名引用规则 如果存在一个包的别名,xmake 将优先考虑包的别名来获得规则。 ```lua add_requires("zlib", {alias = "zlib2", system = false}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib2") add_rules("@zlib2/foo") ``` #### 添加包规则依赖 我们可以使用`add_deps("@bar")`来添加相对于当前包目录的其他规则。 然而,我们不能添加来自其他包的规则依赖,它们是完全隔离的,我们只能参考用户项目中由`add_requires`导入的其他包的规则。 packages/z/zlib/rules/foo.lua ```lua rule("foo") add_deps("@bar") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` packages/z/zlib/rules/bar.lua ```lua rule("bar") on_config(function (target) print("bar: on_config %s", target:name()) end) ``` ### 更加严格的包依赖兼容性支持 我们新增了两个包相关的策略,用于开启更加严格的包依赖兼容性控制。 这主要用于解决一些包每次版本更新,可能都会存在一些 abi 不兼容,或者破坏其他依赖它的包,而默认 Xmake 是不会去重新编译安装它们的,除非它们的版本和配置也被更新了。 这就可能存在一定概率编译兼容性被破坏,导致最终链接失败。 #### package.librarydeps.strict\_compatibility 默认禁用,如果启用它,那么当前包和它的所有库依赖包之间会保持严格的兼容性,任何依赖包的版本更新,都会强制触发当前包的重新编译安装。 以确保所有的包都是二进制兼容的,不会因为某个依赖包接口改动,导致和其他已被安装的其他包一起链接时候,发生链接和运行错误。 ```lua package("foo") add_deps("bar", "zoo") set_policy("package.librarydeps.strict_compatibility", true) ``` 例如,如果 bar 或者 zoo 的版本有更新,那么 foo 也会重新编译安装。 #### package.strict\_compatibility 默认禁用,如果启用它,那么当前包和其他所有依赖它的包之间会保持严格的兼容性,这个包的版本更新,都会强制触发其他父包的重新编译安装。 以确保所有的包都是二进制兼容的,不会因为某个依赖包接口改动,导致和其他已被安装的其他包一起链接时候,发生链接和运行错误。 ```lua package("foo") set_policy("package.strict_compatibility", true) package("bar") add_deps("foo") package("zoo") add_deps("foo") ``` 例如,如果 foo 的版本有更新,那么 bar 和 zoo 都会被强制重新编译安装。 #### package.install\_always 每次运行 `xmake f -c` 重新配置的时候,总是会重新安装包,这对于本地第三方源码包集成时候比较有用。 因为,用户可能随时需要修改第三方源码,然后重新编译集成它们。 之前只能通过每次修改包版本号,来触发重新编译,但是有了这个策略,就能每次都会触发重编。 ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) set_policy("package.install_always", true) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` ### 新增 clang-cl 工具链 尽管之前的版本,我们也支持切换到 clang-cl 编译器,但是切换比较繁琐,得挨个设置。 ```bash $ xmake f --cxx=clang-cl --cc=clang-cl -c $ xmake ``` 而且还得将 clang-cl.exe 所在目录加入 %PATH% 才行。 既然现在 vs 都自带了 clang-cl 工具链,那么 Xmake 完全可以自动检测到并使用它。 因此,在新版本中,我们新增了 clang-cl 工具链,仅仅只需要 `xmake f --toolchain=clang-cl` 就可以快速切换到 clang-cl 工具链,而无需任何 PATH 设置。 ## 更新内容 ### 新特性 * [#2140](https://github.com/xmake-io/xmake/issues/2140): 支持 Windows Arm64 * [#2719](https://github.com/xmake-io/xmake/issues/2719): 添加 `package.librarydeps.strict_compatibility` 策略严格限制包依赖兼容性 * [#2810](https://github.com/xmake-io/xmake/pull/2810): 支持 os.execv 去执行 shell 脚本文件 * [#2817](https://github.com/xmake-io/xmake/pull/2817): 改进规则支持依赖顺序执行 * [#2824](https://github.com/xmake-io/xmake/pull/2824): 传递 cross-file 交叉编译环境给 meson.install 和 trybuild * [#2856](https://github.com/xmake-io/xmake/pull/2856): xrepo 支持从当前指定源码目录调试程序 * [#2859](https://github.com/xmake-io/xmake/issues/2859): 改进对三方库的 trybuild 构建,利用 xmake-repo 仓库脚本更加智能化地构建三方库 * [#2879](https://github.com/xmake-io/xmake/issues/2879): 更好的动态创建和配置 target 和 rule * [#2374](https://github.com/xmake-io/xmake/issues/2374): 允许 xmake 包中引入自定义规则 * 添加 clang-cl 工具链 ### 改进 * [#2745](https://github.com/xmake-io/xmake/pull/2745): 改进 os.cp 支持符号链接复制 * [#2773](https://github.com/xmake-io/xmake/pull/2773): 改进 vcpkg 包安装,支持 freebsd 平台 * [#2778](https://github.com/xmake-io/xmake/pull/2778): 改进 xrepo.env 支持 target 的运行环境加载 * [#2783](https://github.com/xmake-io/xmake/issues/2783): 添加摘要算法选项到 WDK 的 signtool 签名工具 * [#2787](https://github.com/xmake-io/xmake/pull/2787): 改进 json 支持空数组 * [#2782](https://github.com/xmake-io/xmake/pull/2782): 改进查找 matlib sdk 和运行时 * [#2793](https://github.com/xmake-io/xmake/issues/2793): 改进 mconfdialog 配置操作体验 * [#2804](https://github.com/xmake-io/xmake/issues/2804): 安装依赖包支持 macOS arm64/x86\_64 交叉编译 * [#2809](https://github.com/xmake-io/xmake/issues/2809): 改进 msvc 的编译优化选项 * 改进 trybuild 模式,为 meson/autoconf/cmake 提供更好的交叉编译支持 * [#2846](https://github.com/xmake-io/xmake/discussions/2846): 改进对 configfiles 的生成 * [#2866](https://github.com/xmake-io/xmake/issues/2866): 更好地控制 rule 规则执行顺序 ### Bugs 修复 * [#2740](https://github.com/xmake-io/xmake/issues/2740): 修复 msvc 构建 C++ modules 卡死问题 * [#2875](https://github.com/xmake-io/xmake/issues/2875): 修复构建 linux 驱动错误 * [#2885](https://github.com/xmake-io/xmake/issues/2885): 修复 ccache 下,msvc 编译 pch 失败问题 --- --- url: /posts/xmake-update-v2.7.3.md --- ## Introduction of new features ### Package component support #### Introduction This new feature is intended to enable the integration of specific sub-libraries from a C/C++ package, and is generally used for library component integration in larger packages. This is because such packages provide a number of sub-libraries, not all of which are required by the user, and linking them all may be problematic. Although, previous versions were able to support the feature of sublibrary selection, e.g. ```lua add_requires("sfml~foo", {configs = {graphics = true, window = true}}) add_requires("sfml~bar", {configs = {network = true}}) target("foo") set_kind("binary") add_packages("sfml~foo") target("bar") set_kind("binary") add_packages("sfml~bar") ``` This is done by custom configuration of each package, but there are some problems with this approach. 1. `sfml~foo` and `sfml~bar` will be installed repeatedly as two separate packages, taking up double the disk space 2. some common code will be compiled repeatedly, which will affect the efficiency of the installation 3. if a target depends on both `sfml~foo` and `sfml~bar`, there will be link conflicts The impact of double-compilation and disk usage can be very high for very large package integrations such as boost, and can even lead to more than N times the disk usage if there are a large number of sub-library combinations. To solve this problem, Xmake has added a package component mode, which offers some of the following benefits. 1. fast integration of any number of components in just one compile, greatly improving installation efficiency and reducing disk footprint 2. component abstraction, across compilers and platforms, so users don't need to worry about configuring link order dependencies between each sub library 3. easier to use For more background details see: [#2636](https://github.com/xmake-io/xmake/issues/2636) #### Use package components For the user, using package components is very convenient because the user is not required to maintain the package, as long as the package is used, it is configured with the relevant set of components and we can quickly integrate and use it, e.g. ```lua add_requires("sfml") target("foo") set_kind("binary") add_packages("sfml", {components = "graphics"}) target("bar") set_kind("binary") add_packages("sfml", {components = "network"}) ``` #### View package components So how do we know what components are provided by a given package? We can check by executing the following command. ```bash $ xrepo info sfml The package info of project: require(sfml): -> description: Simple and Fast Multimedia Library -> version: 2.5.1 ... -> components: -> system: -> graphics: system, window -> window: system -> audio: system -> network: system ``` #### Package component configuration If you are a package maintainer and want to add component support to a package, then you need to configure the package components via the following two interfaces. * add\_components: adds a list of package components * on\_component: Configures each package component ##### Link configuration for package components In most cases, a package component only needs to be configured with some of its own sub-link information, e.g. ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-graphics" ... e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("links", "freetype") component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) on_component("window", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-window" ... e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) ... ``` The above is an incomplete package configuration, I have only extracted a part of the configuration related to the package components. A full example of the configuration and use of package components can be found at: [components example](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua) ##### Configure compilation information for components We can configure not only the linking information for each component, but also the compilation information for includedirs, defines etc. We can also configure each component individually. ```lua package("sfml") on_component("graphics", function (package, component) package:add("defines", "TEST") end) ``` ##### Configure component dependencies ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) component:add("deps", "window", "system") end) ``` The above configuration tells the package that our graphics component will have additional dependencies on the `window` and `system` components. So, on the user side, our use of the graphics component can be done from the ```lua add_packages("sfml", {components = {"graphics", "window", "system"}) ``` Simplified to. ```lua add_packages("sfml", {components = "graphics") ``` Because, as soon as we turn on the graphics component, it will also automatically enable the dependent window and system components and automatically ensure that the links are in the right order. Alternatively, we can configure component dependencies with `add_components("graphics", {deps = {"window", "system"}})`. ##### Find components from the system library We know that configuring `add_extsources` in the package configuration can improve package discovery on the system, for example by finding libraries from system package managers such as apt/pacman. Of course, we can also make it possible for each component to prioritise finding them from the system repositories via the `extsources` configuration as well. For example, the sfml package, which is actually also componentized in homebrew, can be made to find each component from the system repository without having to install them in source each time. ```bash $ ls -l /usr/local/opt/sfml/lib/pkgconfig -r--r--r-- 1 ruki admin 317 10 19 17:52 sfml-all.pc -r--r--r-- 1 ruki admin 534 10 19 17:52 sfml-audio.pc -r--r--r-- 1 ruki admin 609 10 19 17:52 sfml-graphics.pc -r--r--r-- 1 ruki admin 327 10 19 17:52 sfml-network.pc -r--r--r-- 1 ruki admin 302 10 19 17:52 sfml-system.pc -r--r--r-- 1 ruki admin 562 10 19 17:52 sfml-window.pc ``` We just need, for each component, to configure its extsources: the ```lua if is_plat("macosx") then add_extsources("brew::sfml/sfml-all") end on_component("graphics", function (package, component) -- ... component:add("extsources", "brew::sfml/sfml-graphics") end) ``` ##### Default Global Component Configuration In addition to configuring specific components by specifying component names, if we do not specify a component name, the default is to globally configure all components. ```lua package("sfml") on_component(function (package, component) -- configure all components end) ``` Of course, we could also specify the configuration of the graphics component and the rest of the components would be configured via the default global configuration interface in the following way. ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) -- configure graphics end) on_component(function (package, component) -- component audio, network, window, system end) ``` ### C++ module build improvements #### Incremental build support I thought that Xmake already had good support for C++ modules, but then I realised that its incremental builds don't work properly yet. So this version of Xmake also does a good job of supporting incremental builds of C++ modules, although the support process still took a lot of effort. My analysis shows that the format of the include dependency information (`*.d`) generated with modules varies considerably between the compilers. The gcc format is the most complex, but I got it to support it anyway. ``` build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o: src/foo.mpp\ build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o gcm.cache/foo.gcm: bar.c++m cat.c++m\ foo.c++m: gcm.cache/foo.gcm\ .PHONY: foo.c++m\ gcm.cache/foo.gcm:| build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o\ CXX_IMPORTS += bar.c++m cat.c++m\ ``` clang has the best format compatibility and supports it without any special changes. ``` build//hello.pcm: /usr/lib/llvm-15/lib/clang/15.0.2/include/module.modulemap src/hello.mpp\ ``` The msvc format is more extensible and easier to parse and support: the ``` { "Version": "1.2", "Data": { "Source": "c:\users\ruki\desktop\user_headerunit\src\main.cpp", "ProvidedModule": "", "Includes": [], "ImportedModules": [ { "Name": "hello", "BMI": "c:\users\ruki\desktop\user_headerunit\src\hello.ifc" } ], "ImportedHeaderUnits": [ { "Header": "c:\users\ruki\desktop\user_headerunit\src\header.hpp", "BMI": "c:\users\ruki\desktop\user_headerunit\src\header.hpp.ifc" } ] } } ``` #### Circular Dependency Detection Support As there are dependencies between modules, it is not possible to compile if there are circular dependencies between several modules. However, in previous versions Xmake was unable to detect this, and when a circular dependency was encountered, the compilation would get stuck without any message, which was very unfriendly to the user. In this new version, we have improved this situation by adding the detection of cyclic dependencies for modules, and the following error message will appear when compiling to make it easier for the user to locate the problem. ```bash $ xmake [ 0%]: generating.cxx.module.deps Foo.mpp [ 0%]: generating.cxx.module.deps Foo2.mpp [ 0%]: generating.cxx.module.deps Foo3.mpp [ 0%]: generating.cxx.module.deps main.cpp error: circular modules dependency(Foo2, Foo, Foo3, Foo2) detected! -> module(Foo2) in Foo2.mpp -> module(Foo) in Foo.mpp -> module(Foo3) in Foo3.mpp -> module(Foo2) in Foo2.mpp ``` ### A more LSP friendly syntax format Our default convention of domain configuration syntax, although very clean, is not very friendly to auto-formatted indentation and IDEs, and if you format your configuration, the indentation is completely misplaced. ```lua target("foo") set_kind("binary") add_files("src/*.cpp") ``` Also, if some global configuration is configured between two targets, it does not automatically end the current target scope and the user needs to explicitly call \`\`target\_end()\`. ```lua target("foo") set_kind("binary") add_files("src/*.cpp") target_end() add_defines("ROOT") target("bar") set_kind("binary") add_files("src/*.cpp") ``` Although, as we mentioned above, you can use the `do end` mode to solve the auto-indentation problem, the problem of needing `target_end()` still exists. ```lua target("foo") do set_kind("binary") add_files("src/*.cpp") end target_end() add_defines("ROOT") target("bar") do set_kind("binary") add_files("src/*.cpp") end ``` Therefore, in this new version, we provide a better optional domain configuration syntax to solve the auto-indentation, target domain isolation problem, e.g. ```lua target("foo", function () set_kind("binary") add_files("src/*.cpp") end) add_defines("ROOT") target("bar", function () set_kind("binary") add_files("src/*.cpp") end) ``` The foo and bar fields are completely isolated, so we can configure other settings between them without affecting them, plus it's very LSP friendly and won't cause indentation confusion, even with one-click formatting. Note: This is only an optional extension syntax, the existing configuration syntax is still fully supported and the user can choose the right one according to their needs preferences. ### Add flags to specific compilers Values configured using interfaces such as `add_cflags`, `add_cxxflags`, etc. are usually compiler specific, although Xmake does provide automatic detection and mapping mechanisms. Even if a flags is set that is not supported by the current compiler, Xmake can automatically ignore it, but there will still be a warning. In this new version, we have improved the interface for adding all flags to avoid additional warnings by specifying flags only for specific compilers, e.g. ```lua add_cxxflags("clang::-stdlib=libc++") add_cxxflags("gcc::-stdlib=libc++") ``` Or ```lua add_cxxflags("-stdlib=libc++", {tools = "clang"}) add_cxxflags("-stdlib=libc++", {tools = "gcc"}) ``` Note: Not just compile flags, but also for link flags such as add\_ldflags, which also work. ### renderdoc debugger support Thanks to [@SirLynix](https://github.com/SirLynix) for contributing this great feature which allows Xmake to load renderdoc directly to debug some graphics renderers. It's very simple to use, we first make sure renderdoc is installed, then configure the debugger to renderdoc and load the debug run as follows ```bash $ xmake f --debugger=renderdoc $ xmake run -d ``` The concrete usage effect is as follows. ### New C++ exception interface configuration Xmake has added a new `set_exceptions` abstraction configuration interface, which allows us to configure C++/Objc exceptions to be enabled and disabled. Normally, if we configure them via the add\_cxxflags interface, it would be cumbersome for the compiler to handle them separately, depending on the platform. For example ```lua on_config(function (target) if (target:has_tool("cxx", "cl")) then target:add("cxflags", "/EHsc", {force = true}) target:add("defines", "_HAS_EXCEPTIONS=1", {force = true}) elseif(target:has_tool("cxx", "clang") or target:has_tool("cxx", "clang-cl")) then target:add("cxflags", "-fexceptions", {force = true}) target:add("cxflags", "-fcxx-exceptions", {force = true}) end end) ``` And with this interface, we can abstract to configure them in a compiler-independent way. Enabling C++ exceptions: ```lua set_exceptions("cxx") ``` Disable C++ exceptions: ```lua set_exceptions("no-cxx") ``` We can also configure to turn on objc exceptions at the same time. ```lua set_exceptions("cxx", "objc") ``` or disable them. ```lua set_exceptions("no-cxx", "no-objc") ``` Xmake automatically adapts the flags internally to the different compilers. ### Support for ispc compilation rules Xmake has added support for built-in rules for the ipsc compiler, thanks to [@star-hengxing](https://github.com/star-hengxing), which is used in the following way. ```lua target("test") set_kind("binary") add_rules("utils.ispc", {header_extension = "_ispc.h"}) set_values("ispc.flags", "--target=host") add_files("src/*.ispc") add_files("src/*.cpp") ``` ### Support for msvc's armasm compiler Previous versions of Xmake added initial support for Windows ARM, but did not yet have good support for asm compilation, so in this version we have continued to improve Windows ARM support. Support for msvc's `armasm.exe` and `armasm64.exe` is now available. In addition, we have also improved package cross-compilation support for the Windows ARM platform. ### New gnu-rm build rules Xmake has also added a new rule and example project for building embedded projects using the gnu-rm toolchain, thanks to [@JacobPeng](https://github.com/JacobPeng) for this. ```lua add_rules("mode.debug", "mode.release") add_requires("gnu-rm") set_toolchains("@gnu-rm") set_plat("cross") set_arch("armv7") target("foo") add_rules("gnu-rm.static") add_files("src/foo/*.c") target("hello") add_deps("foo") add_rules("gnu-rm.binary") add_files("src/*.c", "src/*.S") add_files("src/*.ld") add_includedirs("src/lib/cmsis") ``` For the full project see: [Embed GNU-RM Example](https://github.com/xmake-io/xmake/blob/master/tests/projects/embed/gnu-rm/hello/xmake.lua) ### Add OpenBSD system support In previous versions, Xmake only supported FreeBSD, and OpenBSD had a number of differences that prevented Xmake from compiling and installing on it. The new version now fully supports running Xmake on OpenBSD. ## Changelog ### New features * A new optional configuration syntax. It is LSP friendly, automatically calls target\_end() to achieve scope isolation. * [#2944](https://github.com/xmake-io/xmake/issues/2944): Add `gnu-rm.binary` and `gnu-rm.static` rules and tests for embed project * [#2636](https://github.com/xmake-io/xmake/issues/2636): Support package components * Support armasm/armasm64 for msvc * [#3023](https://github.com/xmake-io/xmake/pull/3023): Add support for debugging with renderdoc * [#3022](https://github.com/xmake-io/xmake/issues/3022): Add flags for specific compilers and linkers * [#3025](https://github.com/xmake-io/xmake/pull/3025): C++ exception enabled/disabled switch method * [#3017](https://github.com/xmake-io/xmake/pull/3017): Support ispc compiler ### Changes * [#2925](https://github.com/xmake-io/xmake/issues/2925): Improve doxygen plugin * [#2948](https://github.com/xmake-io/xmake/issues/2948): Support OpenBSD * Add `xmake g --insecure-ssl=y` option to disable ssl certificate when downloading packages * [#2971](https://github.com/xmake-io/xmake/pull/2971): Stabilize vs and vsxmake project generation * [#3000](https://github.com/xmake-io/xmake/issues/3000): Incremental compilation support for modules * [#3016](https://github.com/xmake-io/xmake/pull/3016): Improve clang/msvc to better support std modules ### Bugs fixed * [#2949](https://github.com/xmake-io/xmake/issues/2949): Fix vs group * [#2952](https://github.com/xmake-io/xmake/issues/2952): Fix armlink for long args * [#2954](https://github.com/xmake-io/xmake/issues/2954): Fix c++ module partitions path issue * [#3033](https://github.com/xmake-io/xmake/issues/3033): Detect circular modules dependency --- --- url: /zh/posts/xmake-update-v2.7.3.md --- ## 新特性介绍 ### 包组件支持 #### 背景简介 这个新特性主要用于实现从一个 C/C++ 包中集成特定的子库,一般用于一些比较大的包中的库组件集成。 因为这种包里面提供了很多的子库,但不是每个子库用户都需要,全部链接反而有可能会出问题。 尽管,之前的版本也能够支持子库选择的特性,例如: ```lua add_requires("sfml~foo", {configs = {graphics = true, window = true}}) add_requires("sfml~bar", {configs = {network = true}}) target("foo") set_kind("binary") add_packages("sfml~foo") target("bar") set_kind("binary") add_packages("sfml~bar") ``` 这是通过每个包的自定义配置来实现的,但这种方式会存在一些问题: 1. `sfml~foo` 和 `sfml~bar` 会作为两个独立的包,重复安装,占用双倍的磁盘空间 2. 也会重复编译一些共用代码,影响安装效率 3. 如果一个目标同时依赖了 `sfml~foo` 和 `sfml~bar`,会存在链接冲突 如果是对于 boost 这种超大包的集成,重复编译和磁盘占用的影响会非常大,如果在子库组合非常多的情况下,甚至会导致超过 N 倍的磁盘占用。 为了解决这个问题,Xmake 新增了包组件模式,它提供了以下一些好处: 1. 仅仅一次编译安装,任意多个组件快速集成,极大提升安装效率,减少磁盘占用 2. 组件抽象化,跨编译器和平台,用户不需要关心如何配置每个子库之间链接顺序依赖 3. 使用更加方便 更多背景详情见:[#2636](https://github.com/xmake-io/xmake/issues/2636) #### 使用包组件 对于用户,使用包组件是非常方便的,因为用户是不需要维护包的,只要使用的包,它配置了相关的组件集,我们就可以快速集成和使用它,例如: ```lua add_requires("sfml") target("foo") set_kind("binary") add_packages("sfml", {components = "graphics"}) target("bar") set_kind("binary") add_packages("sfml", {components = "network"}) ``` #### 查看包组件 那么,如何知道指定的包提供了哪些组件呢?我们可以通过执行下面的命令查看: ```bash $ xrepo info sfml The package info of project: require(sfml): -> description: Simple and Fast Multimedia Library -> version: 2.5.1 ... -> components: -> system: -> graphics: system, window -> window: system -> audio: system -> network: system ``` #### 包组件配置 如果你是包的维护者,想要将一个包增加组件支持,那么需要通过下面两个接口来完成包组件的配置: * add\_components: 添加包组件列表 * on\_component: 配置每个包组件 ##### 包组件的链接配置 大多数情况下,包组件只需要配置它自己的一些子链接信息,例如: ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-graphics" .. e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("links", "freetype") component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) on_component("window", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-window" .. e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) ... ``` 上面是一个不完整的包配置,我仅仅摘取一部分跟包组件相关的配置。 一个关于包组件的配置和使用的完整例子见:[components example](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua) ##### 配置组件的编译信息 我们不仅可以配置每个组件的链接信息,还有 includedirs, defines 等等编译信息,我们也可以对每个组件单独配置。 ```lua package("sfml") on_component("graphics", function (package, component) package:add("defines", "TEST") end) ``` ##### 配置组件依赖 ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) component:add("deps", "window", "system") end) ``` 上面的配置,告诉包,我们的 graphics 组件还会额外依赖 `window` 和 `system` 两个组件。 因此,在用户端,我们对 graphics 的组件使用,可以从 ```lua add_packages("sfml", {components = {"graphics", "window", "system"}) ``` 简化为: ```lua add_packages("sfml", {components = "graphics") ``` 因为,只要我们开启了 graphics 组件,它也会自动启用依赖的 window 和 system 组件,并且自动保证链接顺序正确。 另外,我们也可以通过 `add_components("graphics", {deps = {"window", "system"}})` 来配置组件依赖关系。 ##### 从系统库中查找组件 我们知道,在包配置中,配置 `add_extsources` 可以改进包在系统中的查找,比如从 apt/pacman 等系统包管理器中找库。 当然,我们也可以让每个组件也能通过 `extsources` 配置,去优先从系统库中找到它们。 例如,sfml 包,它在 homebrew 中其实也是组件化的,我们完全可以让包从系统库中,找到对应的每个组件,而不需要每次源码安装它们。 ```bash $ ls -l /usr/local/opt/sfml/lib/pkgconfig -r--r--r-- 1 ruki admin 317 10 19 17:52 sfml-all.pc -r--r--r-- 1 ruki admin 534 10 19 17:52 sfml-audio.pc -r--r--r-- 1 ruki admin 609 10 19 17:52 sfml-graphics.pc -r--r--r-- 1 ruki admin 327 10 19 17:52 sfml-network.pc -r--r--r-- 1 ruki admin 302 10 19 17:52 sfml-system.pc -r--r--r-- 1 ruki admin 562 10 19 17:52 sfml-window.pc ``` 我们只需要,对每个组件配置它的 extsources: ```lua if is_plat("macosx") then add_extsources("brew::sfml/sfml-all") end on_component("graphics", function (package, component) -- ... component:add("extsources", "brew::sfml/sfml-graphics") end) ``` ##### 默认的全局组件配置 除了通过指定组件名的方式,配置特定组件,如果我们没有指定组件名,默认就是全局配置所有组件。 ```lua package("sfml") on_component(function (package, component) -- configure all components end) ``` 当然,我们也可以通过下面的方式,指定配置 graphics 组件,剩下的组件通过默认的全局配置接口进行配置: ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) -- configure graphics end) on_component(function (package, component) -- component audio, network, window, system end) ``` ### C++ 模块构建改进 #### 增量构建支持 原本以为 Xmake 对 C++ 模块已经支持的比较完善了,后来才发现,它的增量编译还无法正常工作。 因此,这个版本 Xmake 对 C++ 模块的增量编译也做了很好的支持,尽管支持过程还是花了很多精力的。 我分析了下,各家的编译器对生成带模块的 include 依赖信息格式(`*.d`),差异还是非常大的。 gcc 的格式最复杂,不过我还是将它支持上了。 ``` build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o: src/foo.mpp\ build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o gcm.cache/foo.gcm: bar.c++m cat.c++m\ foo.c++m: gcm.cache/foo.gcm\ .PHONY: foo.c++m\ gcm.cache/foo.gcm:| build/.objs/dependence/linux/x86_64/release/src/foo.mpp.o\ CXX_IMPORTS += bar.c++m cat.c++m\ ``` clang 的格式兼容性最好,没有做任何特殊改动就支持了。 ``` build//hello.pcm: /usr/lib/llvm-15/lib/clang/15.0.2/include/module.modulemap src/hello.mpp\ ``` msvc 的格式扩展性比较好,解析和支持起来比较方便: ``` { "Version": "1.2", "Data": { "Source": "c:\users\ruki\desktop\user_headerunit\src\main.cpp", "ProvidedModule": "", "Includes": [], "ImportedModules": [ { "Name": "hello", "BMI": "c:\users\ruki\desktop\user_headerunit\src\hello.ifc" } ], "ImportedHeaderUnits": [ { "Header": "c:\users\ruki\desktop\user_headerunit\src\header.hpp", "BMI": "c:\users\ruki\desktop\user_headerunit\src\header.hpp.ifc" } ] } } ``` #### 循环依赖检测支持 由于模块之间是存在依赖关系的,因此如果有几个模块之间存在循环依赖引用,那么是无法编译通过的。 但是之前的版本中,Xmake 无法检测到这种情况,遇到循环依赖,编译就会卡死,没有任何提示信息,这对用户非常不友好。 而新版本中,我们对这种情况做了改进,增加了模块的循环依赖检测,编译时候会出现以下错误提示,方便用户定位问题: ```bash $ xmake [ 0%]: generating.cxx.module.deps Foo.mpp [ 0%]: generating.cxx.module.deps Foo2.mpp [ 0%]: generating.cxx.module.deps Foo3.mpp [ 0%]: generating.cxx.module.deps main.cpp error: circular modules dependency(Foo2, Foo, Foo3, Foo2) detected! -> module(Foo2) in Foo2.mpp -> module(Foo) in Foo.mpp -> module(Foo3) in Foo3.mpp -> module(Foo2) in Foo2.mpp ``` ### 更加 LSP 友好的语法格式 我们默认约定的域配置语法,尽管非常简洁,但是对自动格式化缩进和 IDE 不是很友好,如果你格式化配置,缩进就完全错位了。 ```lua target("foo") set_kind("binary") add_files("src/*.cpp") ``` 另外,如果两个 target 之间配置了一些全局的配置,那么它不能自动结束当前 target 作用域,用户需要显式调用 `target_end()`。 ```lua target("foo") set_kind("binary") add_files("src/*.cpp") target_end() add_defines("ROOT") target("bar") set_kind("binary") add_files("src/*.cpp") ``` 虽然,上面我们提到,可以使用 `do end` 模式来解决自动缩进问题,但是需要 `target_end()` 的问题还是存在。 ```lua target("foo") do set_kind("binary") add_files("src/*.cpp") end target_end() add_defines("ROOT") target("bar") do set_kind("binary") add_files("src/*.cpp") end ``` 因此,在新版本中,我们提供了一种更好的可选域配置语法,来解决自动缩进,target 域隔离问题,例如: ```lua target("foo", function () set_kind("binary") add_files("src/*.cpp") end) add_defines("ROOT") target("bar", function () set_kind("binary") add_files("src/*.cpp") end) ``` foo 和 bar 两个域是完全隔离的,我们即使在它们中间配置其他设置,也不会影响它们,另外,它还对 LSP 非常友好,即使一键格式化,也不会导致缩进混乱。 注:这仅仅只是一只可选的扩展语法,现有的配置语法还是完全支持的,用户可以根据自己的需求喜好,来选择合适的配置语法。 ### 为特定编译器添加 flags 使用 `add_cflags`, `add_cxxflags` 等接口配置的值,通常都是跟编译器相关的,尽管 Xmake 也提供了自动检测和映射机制, 即使设置了当前编译器不支持的 flags,Xmake 也能够自动忽略它,但是还是会有警告提示。 新版本中,我们改进了所有 flags 添加接口,可以仅仅对特定编译器指定 flags,来避免额外的警告,例如: ```lua add_cxxflags("clang::-stdlib=libc++") add_cxxflags("gcc::-stdlib=libc++") ``` 或者: ```lua add_cxxflags("-stdlib=libc++", {tools = "clang"}) add_cxxflags("-stdlib=libc++", {tools = "gcc"}) ``` 注:不仅仅是编译flags,对 add\_ldflags 等链接 flags,也是同样生效的。 ### renderdoc 调试器支持 感谢 [@SirLynix](https://github.com/SirLynix) 贡献了这个很棒的特性,它可以让 Xmake 直接加载 renderdoc 去调试一些图形渲染程序。 使用非常简单,我们先确保安装了 renderdoc,然后配置调试器为 renderdoc,加载调试运行: ```bash $ xmake f --debugger=renderdoc $ xmake run -d ``` 具体使用效果如下: ### 新增 C++ 异常接口配置 Xmake 新增了一个 `set_exceptions` 抽象化配置接口,我们可以通过这个配置,配置启用和禁用 C++/Objc 的异常。 通常,如果我们通过 add\_cxxflags 接口去配置它们,需要根据不同的平台,编译器分别处理它们,非常繁琐。 例如: ```lua on_config(function (target) if (target:has_tool("cxx", "cl")) then target:add("cxflags", "/EHsc", {force = true}) target:add("defines", "_HAS_EXCEPTIONS=1", {force = true}) elseif(target:has_tool("cxx", "clang") or target:has_tool("cxx", "clang-cl")) then target:add("cxflags", "-fexceptions", {force = true}) target:add("cxflags", "-fcxx-exceptions", {force = true}) end end) ``` 而通过这个接口,我们就可以抽象化成编译器无关的方式去配置它们。 开启 C++ 异常: ```lua set_exceptions("cxx") ``` 禁用 C++ 异常: ```lua set_exceptions("no-cxx") ``` 我们也可以同时配置开启 objc 异常。 ```lua set_exceptions("cxx", "objc") ``` 或者禁用它们。 ```lua set_exceptions("no-cxx", "no-objc") ``` Xmake 会在内部自动根据不同的编译器,去适配对应的 flags。 ### 支持 ispc 编译规则 Xmake 新增了 ipsc 编译器内置规则支持,非常感谢 [@star-hengxing](https://github.com/star-hengxing) 的贡献,具体使用方式如下: ```lua target("test") set_kind("binary") add_rules("utils.ispc", {header_extension = "_ispc.h"}) set_values("ispc.flags", "--target=host") add_files("src/*.ispc") add_files("src/*.cpp") ``` ### 支持 msvc 的 armasm 编译器 之前的版本,Xmake 增加了 Windows ARM 的初步支持,但是对 asm 编译还没有很好的支持,因此这个版本,我们继续完善 Windows ARM 的支持。 对 msvc 的 `armasm.exe` 和 `armasm64.exe` 都支持上了。 另外,我们也改进了包对 Windows ARM 平台的交叉编译支持。 ### 新增 gnu-rm 构建规则 Xmake 也新增了一个使用 gnu-rm 工具链去构建嵌入式项目的规则和例子工程,非常感谢 [@JacobPeng](https://github.com/JacobPeng) 的贡献。 ```lua add_rules("mode.debug", "mode.release") add_requires("gnu-rm") set_toolchains("@gnu-rm") set_plat("cross") set_arch("armv7") target("foo") add_rules("gnu-rm.static") add_files("src/foo/*.c") target("hello") add_deps("foo") add_rules("gnu-rm.binary") add_files("src/*.c", "src/*.S") add_files("src/*.ld") add_includedirs("src/lib/cmsis") ``` 完整工程见:[Embed GNU-RM Example](https://github.com/xmake-io/xmake/blob/master/tests/projects/embed/gnu-rm/hello/xmake.lua) ### 新增 OpenBSD 系统支持 之前的版本,Xmake 仅仅支持 FreeBSD 系统,而 OpenBSD 跟 FreeBSD 还是有不少差异的,导致 Xmake 无法在它上面正常编译安装。 而新版本已经完全支持在 OpenBSD 上运行 Xmake 了。 ## 更新内容 ### 新特性 * 一种新的可选域配置语法,对 LSP 友好,并且支持域隔离。 * [#2944](https://github.com/xmake-io/xmake/issues/2944): 为嵌入式工程添加 `gnu-rm.binary` 和 `gnu-rm.static` 规则和测试工程 * [#2636](https://github.com/xmake-io/xmake/issues/2636): 支持包组件 * 支持 msvc 的 armasm/armasm64 * [#3023](https://github.com/xmake-io/xmake/pull/3023): 改进 xmake run -d,添加 renderdoc 调试器支持 * [#3022](https://github.com/xmake-io/xmake/issues/3022): 为特定编译器添加 flags * [#3025](https://github.com/xmake-io/xmake/pull/3025): 新增 C++ 异常接口配置 * [#3017](https://github.com/xmake-io/xmake/pull/3017): 支持 ispc 编译器规则 ### 改进 * [#2925](https://github.com/xmake-io/xmake/issues/2925): 改进 doxygen 插件 * [#2948](https://github.com/xmake-io/xmake/issues/2948): 支持 OpenBSD * 添加 `xmake g --insecure-ssl=y` 配置选项去禁用 ssl 证书检测 * [#2971](https://github.com/xmake-io/xmake/pull/2971): 使 vs/vsxmake 工程生成的结果每次保持一致 * [#3000](https://github.com/xmake-io/xmake/issues/3000): 改进 C++ 模块构建支持,实现增量编译支持 * [#3016](https://github.com/xmake-io/xmake/pull/3016): 改进 clang/msvc 去更好地支持 std 模块 ### Bugs 修复 * [#2949](https://github.com/xmake-io/xmake/issues/2949): 修复 vs 分组 * [#2952](https://github.com/xmake-io/xmake/issues/2952): 修复 armlink 处理长命令失败问题 * [#2954](https://github.com/xmake-io/xmake/issues/2954): 修复 c++ module partitions 路径无效问题 * [#3033](https://github.com/xmake-io/xmake/issues/3033): 探测循环模块依赖 --- --- url: /posts/xmake-update-v2.7.6.md --- ## Introduction of new features ### Support Verilog Program #### iVerilog Simulator Through `add_requires("iverilog")` configuration, we can automatically pull the iverilog toolchain package, and then use `set_toolchains("@iverilog")` to automatically bind the toolchain to compile the project. ```lua add_requires("iverilog") target("hello") add_rules("iverilog. binary") set_toolchains("@iverilog") add_files("src/*.v") ``` ##### Set abstract configuration ```Lua add_requires("iverilog") target("hello") add_rules("iverilog. binary") set_toolchains("@iverilog") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` We can use `set_languages("v1800-2009")` to set the language standard for switching Verilog. Currently supported values and mappings are as follows: ```lua ["v1364-1995"] = "-g1995" ["v1364-2001"] = "-g2001" ["v1364-2005"] = "-g2005" ["v1800-2005"] = "-g2005-sv" ["v1800-2009"] = "-g2009" ["v1800-2012"] = "-g2012" ``` ##### Set custom flags ```lua add_requires("iverilog") target("hello") add_rules("iverilog. binary") set_toolchains("@iverilog") add_files("src/*.v") add_values("iverilogs.flags", "-DTEST") ``` ##### Build the project ```console $ xmake check iverilog... iverilog check vvp... vvp [50%]: linking.iverilog hello.vvp [100%]: build ok! ``` ##### Run the program ```console $ xmake run hello world! LXT2 INFO: dumpfile hello.vcd opened, ready for output. src/main.v:6: $finish called at 0 (1s) ``` More complete examples: [iVerilog Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/iverilog) #### Verilator Simulator Through `add_requires("verilator")` configuration, we can automatically pull the verilator toolchain package, and then use `set_toolchains("@verilator")` to automatically bind to the toolchain to compile the project. ```lua add_requires("verilator") target("Hello") add_rules("verilator. binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") ``` verilator project, we need an additional `sim_main.cpp` file to participate in the compilation, as the entry code of the program. ```c #include "hello.h" #include "verilated.h" (Simplified Chinese) int main(int argc, char** argv) { VerilatedContext* contextp = new VerilatedContext; contextp->commandArgs(argc, argv); hello* top = new hello{contextp}; while (!contextp->gotFinish()) { top->eval(); } remove top. Remove contextp. returns 0. } ``` ##### Set abstract configuration ```lua add_requires("verilator") target("Hello") add_rules("verilator. binary") set_toolchains("@verilator") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` We can use `set_languages("v1800-2009")` to set the language standard for switching Verilog. Currently supported values and mappings are as follows. ```lua --Verilog ["v1364-1995"] = "+1364-1995ext+v". ["v1364-2001"] = "+1364-2001ext+v". ["v1364-2005"] = "+1364-2005ext+v". --system-Verilog ["v1800-2005"] = "+1800-2005ext+v". ["v1800-2009"] = "+1800-2009ext+v". ["v1800-2012"] = "+1800-2012ext+v", ["v1800-2017"] = "+1800-2017ext+v". ``` ##### Set custom flags ```lua add_requires("verilator") target("Hello") add_rules("verilator. binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") add_values("verilator.flags", "--trace", "--timing") ``` ##### Build the project ```console $ xmake [ 0%]: compiling.verilog src/main.v [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello.cpp [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated_threads.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello__Syms.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h07139e86__0.cpp [15%]: cache compiling.release src/sim_main.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0.cpp [84%]: linking. release hello [100%]: build ok! ``` ##### Run the program ```console $ xmake run ruki-2:hello ruki$ xmake run hello world! - src/main.v:4:Verilog $finish ``` A more complete example: [Verilator](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/verilator) ### Support for C++ Module distribution Many thanks to [Arthapz](https://github.com/Arthapz) for continuing to help improve xmake's support for C++ Modules in this new release. We can now distribute C++ Modules as packages for quick integration and reuse in other projects. This is a prototype implementation based on the draft design for module distribution in [p2473r1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2473r1.pdf). #### Creating a C++ Modules package for distribution We start by maintaining a build of the modules using xmake.lua and telling xmake which module files to install for external distribution by specifying \`\`{install = true}\`''. ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") target("foo") set_kind("static") add_files("*.cpp") add_files("*.mpp", { install = true }) ``` We then make it into a package that we can commit to the [xmake-repo](https://github.com/xmake-io/xmake-repo) repository, or of course directly into a local package, or a private repository package. Here, for testing purposes, we just make it a local package via `set_sourcedir`. ```lua package("foo") set_sourcedir(path.join(os.scriptdir(), "src")) on_install(function(package) import("package.tools.xmake").install(package, {}) end) ``` #### Integrating the C++ Modules package We then quickly integrate the C++ Modules package for use via the package integration interface with `add_requires("foo")`. Since the modules packages for foo are defined in a private repository, we introduce our own package repository via `add_repositories("my-repo my-repo")`. If the package has already been committed to the official xmake-repo repository, there is no need to configure it additionally. ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") add_repositories("my-repo my-repo") add_requires("foo", "bar") target("packages") set_kind("binary") add_files("src/*.cpp") add_packages("foo", "bar") set_policy("build.c++.modules", true) ``` Once the packages are integrated, we can run the \`\`xmake\`'' command to download, compile and integrate the C++ Modules package for use with one click. ````bash $ xmake checking for platform ... linux checking for architecture ... x86_64 note: install or modify (m) these packages (pass -y to skip confirm)? in my-repo: -> foo latest -> bar latest please input: y (y/n/m) => install bar latest ... ok => install foo latest ... ok [ 0%]: generating.module.deps src/main.cpp [ 0%]: generating.module.deps /mnt/xmake/tests/projects/c++/modules/packages/build/.packages/b/bar/latest/ 4e0143c97b65425b855ad5fd03038b6a/modules/bar/bar.mpp [ 0%]: generating.module.deps /mnt/xmake/tests/projects/c++/modules/packages/build/.packages/f/foo/latest/ 4e0143c97b65425b855ad5fd03038b6a/modules/foo/foo.mpp [ 14%]: compiling.module.release bar [ 14%]: compiling.module.release foo [ 57%]: compiling.release src/main.cpp [ 71%]: linking.release packages [ 100%]: build ok! ```'' Note: After each package is installed, a meta-info file for the maintenance module is stored in the package path, this is a format specification agreed in ``p2473r1.pdf``, it may not be the final standard, but this does not affect our ability to use the distribution of the module now. ```bash $ cat . /build/.packages/f/f/foo/latest/4e0143c97b65425b855ad5fd03038b6a/modules/foo/foo.mpp.meta-info {"_VENDOR_extension":{"xmake":{"name": "foo", "file": "foo.mpp"}}, "definitions":{}, "include_paths":{}} ```` The full example project is available at: [C++ Modules package distribution example project](https://github.com/xmake-io/xmake/tree/master/tests/projects/c%2B%2B/modules/packages) ### Support for C++23 Std Modules [Arthapz](https://github.com/Arthapz) has also helped to improve support for C++23 Std Modules. It is currently supported by three compilers in progress. #### Msvc The latest Visual Studio 17.5 preview already supports it, and the non-standard ifc std modules will be deprecated. For the standard C++23 std modules, this is how we introduced them. ```c import std; ``` Whereas for ifc std modules, we need to write it like this. ``` import std.core; ``` This is not a C++23 standard, it is only provided by msvc, it is not compatible with other compilers and will be deprecated in new versions of msvc. Therefore the new version of Xmake will only support C++23 std modules and not the deprecated ifc std modules. #### Clang It seems that the latest clang does not yet fully support C++23 std modules either, and is still in draft patch status, [#D135507](https://reviews.llvm.org/D135507). However, Xmake does support it, so if you want to try it out, you can merge in the patch and test it with xmake. There is also experimental support for non-standard std modules in lower versions of clang. It is still possible to experiment with xmake to build std modules in lower versions of clang, even though it is probably still a toy (and will encounter many problems). For a discussion see: [#3255](https://github.com/xmake-io/xmake/pull/3255) #### Gcc It is not currently supported. ### Xrepo auto-completion support Previously, we only supported the incomplete xmake command. In this new version, we also support the incomplete `xrepo install` command, which This will automatically search the [xmake-repo](https://github.com/xmake-io/xmake-repo) repository for packages to incomplete our install command. Many thanks to @glcraft for this contribution. ```bash $ xrepo install libp libpaper libpfm libpng libpqxx libpthread-stubs libpcap libplist libpq libpsl ``` ## Changelog ### New features * [#3228](https://github.com/xmake-io/xmake/pull/3228): Add support of importing modules from packages * [#3257](https://github.com/xmake-io/xmake/issues/3257): Add support for iverilog and verilator * Support for xp and vc6.0 * [#3214](https://github.com/xmake-io/xmake/pull/3214): Completion on xrepo install packages ### Changes * [#3255](https://github.com/xmake-io/xmake/pull/3225): Improve clang libc++ module support * Support for compiling xmake using mingw * Improve compatibility issues with xmake running on win xp * Add pure lua json implementation instead of lua-cjson if the external dependencies are enabled ### Bugs fixed * [#3229](https://github.com/xmake-io/xmake/issues/3229): Fix find rc.exe for vs2015 * [#3271](https://github.com/xmake-io/xmake/issues/3271): Fix macro defines with spaces * [#3273](https://github.com/xmake-io/xmake/issues/3273): Fix nim link error * [#3286](https://github.com/xmake-io/xmake/issues/3286): Fix compile\_commands for clangd --- --- url: /zh/posts/xmake-update-v2.7.6.md --- ## 新特性介绍 ### Verilog 仿真程序支持 #### iVerilog 仿真器 通过 `add_requires("iverilog")` 配置,我们能够自动拉取 iverilog 工具链包,然后使用 `set_toolchains("@iverilog")` 自动绑定工具链来编译工程。 ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") ``` ##### 设置抽象配置 ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` 我们可以通过 `set_languages("v1800-2009")` 来设置切换 Verilog 的语言标准。 目前支持的一些取值和映射关系如下: ```lua ["v1364-1995"] = "-g1995" ["v1364-2001"] = "-g2001" ["v1364-2005"] = "-g2005" ["v1800-2005"] = "-g2005-sv" ["v1800-2009"] = "-g2009" ["v1800-2012"] = "-g2012" ``` ##### 设置自定义 flags ```lua add_requires("iverilog") target("hello") add_rules("iverilog.binary") set_toolchains("@iverilog") add_files("src/*.v") add_values("iverilogs.flags", "-DTEST") ``` ##### 构建工程 ```console $ xmake checking for iverilog ... iverilog checking for vvp ... vvp [ 50%]: linking.iverilog hello.vvp [100%]: build ok! ``` ##### 运行程序 ```console $ xmake run hello world! LXT2 info: dumpfile hello.vcd opened for output. src/main.v:6: $finish called at 0 (1s) ``` 更多完整例子:[iVerilog Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/iverilog) #### Verilator 仿真器 通过 `add_requires("verilator")` 配置,我们能够自动拉取 verilator 工具链包,然后使用 `set_toolchains("@verilator")` 自动绑定到工具链来编译工程。 ```lua add_requires("verilator") target("hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") ``` verilator 工程,我们需要一个额外的 `sim_main.cpp` 文件参与编译,作为程序的入口代码。 ``` #include "hello.h" #include "verilated.h" int main(int argc, char** argv) { VerilatedContext* contextp = new VerilatedContext; contextp->commandArgs(argc, argv); hello* top = new hello{contextp}; while (!contextp->gotFinish()) { top->eval(); } delete top; delete contextp; return 0; } ``` ##### 设置抽象配置 ```lua add_requires("verilator") target("hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_defines("TEST") add_includedirs("inc") set_languages("v1800-2009") ``` 我们可以通过 `set_languages("v1800-2009")` 来设置切换 Verilog 的语言标准。 目前支持的一些取值和映射关系如下: ```lua -- Verilog ["v1364-1995"] = "+1364-1995ext+v", ["v1364-2001"] = "+1364-2001ext+v", ["v1364-2005"] = "+1364-2005ext+v", -- SystemVerilog ["v1800-2005"] = "+1800-2005ext+v", ["v1800-2009"] = "+1800-2009ext+v", ["v1800-2012"] = "+1800-2012ext+v", ["v1800-2017"] = "+1800-2017ext+v", ``` ##### 设置自定义 flags ```lua add_requires("verilator") target("hello") add_rules("verilator.binary") set_toolchains("@verilator") add_files("src/*.v") add_files("src/*.cpp") add_values("verilator.flags", "--trace", "--timing") ``` ##### 构建工程 ```console $ xmake [ 0%]: compiling.verilog src/main.v [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0__Slow.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello.cpp [ 15%]: cache compiling.release /Users/ruki/.xmake/packages/v/verilator/2023.1.10/cd2268409c1d44799288c7759b3cbd56/share/verilator/include/verilated_threads.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello__Syms.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h07139e86__0.cpp [ 15%]: cache compiling.release src/sim_main.cpp [ 15%]: cache compiling.release build/.gens/hello/macosx/x86_64/release/rules/verilator/hello___024root__DepSet_h9053a130__0.cpp [ 84%]: linking.release hello [100%]: build ok! ``` ##### 运行程序 ```console $ xmake run ruki-2:hello ruki$ xmake run hello world! - src/main.v:4: Verilog $finish ``` 更多完整例子:[Verilator](https://github.com/xmake-io/xmake/tree/master/tests/projects/embed/verilator) ### 支持 C++ Module 分发 非常感谢 [Arthapz](https://github.com/Arthapz) 在新版本中继续帮忙改进了 xmake 对 C++ Modules 的支持。 现在,我们可以将 C++ Modules 做成包进行分发,然后在其他项目中进行快速集成和复用。 它是基于 [p2473r1](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2473r1.pdf) 中对模块分发的设计草案做的一个原型实现。 #### 制作分发 C++ Modules 包 我们先使用 xmake.lua 维护模块的构建,并通过指定 `{install = true}`,来告诉 xmake 哪些模块文件需要安装对外分发。 ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") target("foo") set_kind("static") add_files("*.cpp") add_files("*.mpp", { install = true }) ``` 然后,我们把它做成包,可以提交到 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库,当然也可以直接做成本地包,或者私有仓库包。 这里,为了方便测试验证,我们仅仅通过 `set_sourcedir` 将它做成本地包。 ```lua package("foo") set_sourcedir(path.join(os.scriptdir(), "src")) on_install(function(package) import("package.tools.xmake").install(package, {}) end) ``` #### 集成 C++ Modules 包 然后,我们通过 `add_requires("foo")` 的包集成接口,对 C++ Modules 包进行快速集成使用。 由于 foo 的模块包,我们放在私有仓库中定义,所以我们通过 `add_repositories("my-repo my-repo")` 引入自己的包仓库。 如果,包已经提交到 xmake-repo 官方仓库,就不需要额外配置它。 ```lua add_rules("mode.release", "mode.debug") set_languages("c++20") add_repositories("my-repo my-repo") add_requires("foo", "bar") target("packages") set_kind("binary") add_files("src/*.cpp") add_packages("foo", "bar") set_policy("build.c++.modules", true) ``` 集成好包后,我们就可以执行 `xmake` 命令,一键下载、编译、集成 C++ Modules 包来使用。 ```bash $ xmake checking for platform ... linux checking for architecture ... x86_64 note: install or modify (m) these packages (pass -y to skip confirm)? in my-repo: -> foo latest -> bar latest please input: y (y/n/m) => install bar latest .. ok => install foo latest .. ok [ 0%]: generating.module.deps src/main.cpp [ 0%]: generating.module.deps /mnt/xmake/tests/projects/c++/modules/packages/build/.packages/b/bar/latest/4e0143c97b65425b855ad5fd03038b6a/modules/bar/bar.mpp [ 0%]: generating.module.deps /mnt/xmake/tests/projects/c++/modules/packages/build/.packages/f/foo/latest/4e0143c97b65425b855ad5fd03038b6a/modules/foo/foo.mpp [ 14%]: compiling.module.release bar [ 14%]: compiling.module.release foo [ 57%]: compiling.release src/main.cpp [ 71%]: linking.release packages [100%]: build ok! ``` 注:每个包安装后,会在包路径下,存储维护模块的 meta-info 文件,这是 `p2473r1.pdf` 中约定的一种格式规范,也许它不是最终的标准,但这并不影响我们现在去使用模块的分发。 ```bash $ cat ./build/.packages/f/foo/latest/4e0143c97b65425b855ad5fd03038b6a/modules/foo/foo.mpp.meta-info {"_VENDOR_extension":{"xmake":{"name":"foo","file":"foo.mpp"}},"definitions":{},"include_paths":{}} ``` 完整的例子工程见:[C++ Modules 包分发例子工程](https://github.com/xmake-io/xmake/tree/master/tests/projects/c%2B%2B/modules/packages) ### 支持 C++23 Std Modules [Arthapz](https://github.com/Arthapz) 也帮忙改进了对 C++23 Std Modules 的支持。 目前三个编译器对它的支持进展: #### Msvc 最新 Visual Studio 17.5 preview 已经支持,并且非标准的 ifc std modules 将被废弃。 对于标准的 C++23 std modules,我们是这么引入的。 ```c import std; ``` 而对于 ifc std modules,我们需要这么写: ``` import std.core; ``` 它不是 C++23 标准,仅仅 msvc 提供,对其他编译器并不兼容,以后新版本 msvc 中也会逐步废弃。 因此新版本 Xmake 将仅仅 C++23 std modules,不再支持废弃的 ifc std modules。 #### Clang 目前最新的 clang 似乎也还没完全支持 C++23 std modules,当前还是 draft patch 状态,[#D135507](https://reviews.llvm.org/D135507)。 但是,Xmake 也对它进行了支持,如果大家想要尝鲜,可以自行合入这个 patch,然后使用 xmake 来测试。 另外,低版本的 clang 也有对非标准的 std modules 做了实验性支持。 我们还是可以在低版本 clang 中尝试性使用 xmake 来构建 std modules,尽管它可能还只是个玩具(会遇到很多问题)。 相关讨论见:[#3255](https://github.com/xmake-io/xmake/pull/3255) #### Gcc 目前还不支持。 ### Xrepo 自动补全支持 之前,我们仅仅支持 xmake 命令的不全,新版本中,我们还支持了 `xrepo install` 命令的不全, 可以自动搜索 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库的包,来不全我们的安装命令。 非常感谢 @glcraft 的贡献。 ```bash $ xrepo install libp libpaper libpfm libpng libpqxx libpthread-stubs libpcap libplist libpq libpsl ``` ## 更新内容 ### 新特性 * [#3228](https://github.com/xmake-io/xmake/pull/3228): C++ modules 的安装发布,以及从包中导入 C++ modules 支持 * [#3257](https://github.com/xmake-io/xmake/issues/3257): 增加对 iverilog 和 verilator 的支持 * 支持 xp 和 vc6.0 * [#3214](https://github.com/xmake-io/xmake/pull/3214): xrepo install 的自动补全支持 ### 改进 * [#3255](https://github.com/xmake-io/xmake/pull/3225): 改进 clang libc++ 模块支持 * 支持使用 mingw 编译 xmake * 改进 xmake 在 win xp 上的兼容性 * 如果外部依赖被启用,切换 json 模块到纯 lua 实现,移除对 lua-cjson 的依赖 ### Bugs 修复 * [#3229](https://github.com/xmake-io/xmake/issues/3229): 修复 vs2015 下找不到 rc.exe 问题 * [#3271](https://github.com/xmake-io/xmake/issues/3271): 修复支持带有空格的宏定义 * [#3273](https://github.com/xmake-io/xmake/issues/3273): 修复 nim 链接错误 * [#3286](https://github.com/xmake-io/xmake/issues/3286): 修复 compile\_commands 对 clangd 的支持 --- --- url: /posts/xmake-update-v2.7.7.md --- ### Improve target configuration source analysis We have improved the presentation of target information in the `xmake show -t target` command by adding a new configuration source analysis and streamlining some of the relatively redundant information. We can use it to better troubleshoot where some of the flags we configure actually come from. The display looks like this. ```bash $ xmake show -t tbox The information of target(tbox): at: /Users/ruki/projects/personal/tbox/src/tbox/xmake.lua kind: static targetfile: build/macosx/x86_64/release/libtbox.a rules: -> mode.release -> ./xmake.lua:26 -> mode.debug -> ./xmake.lua:26 -> utils.install.cmake_importfiles -> ./src/tbox/xmake.lua:15 -> utils.install.pkgconfig_importfiles -> ./src/tbox/xmake.lua:16 options: -> object -> ./src/tbox/xmake.lua:53 -> charset -> ./src/tbox/xmake.lua:53 -> database -> ./src/tbox/xmake.lua:53 packages: -> mysql -> ./src/tbox/xmake.lua:43 -> sqlite3 -> ./src/tbox/xmake.lua:43 links: -> pthread -> option(__keyword_thread_local) -> @programdir/includes/check_csnippets.lua:100 syslinks: -> pthread -> ./xmake.lua:71 -> dl -> ./xmake.lua:71 -> m -> ./xmake.lua:71 -> c -> ./xmake.lua:71 defines: -> __tb_small__ -> ./xmake.lua:42 -> __tb_prefix__="tbox" -> ./src/tbox/xmake.lua:19 -> _GNU_SOURCE=1 -> option(__systemv_semget) -> @programdir/includes/check_cfuncs.lua:104 cxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:22 -> -fno-strict-aliasing -> ./xmake.lua:22 -> -Wno-error=expansion-to-defined -> ./xmake.lua:22 -> -fno-stack-protector -> ./xmake.lua:51 frameworks: -> CoreFoundation -> ./src/tbox/xmake.lua:38 -> CoreServices -> ./src/tbox/xmake.lua:38 mxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:23 -> -fno-strict-aliasing -> ./xmake.lua:23 -> -Wno-error=expansion-to-defined -> ./xmake.lua:23 includedirs: -> src -> ./src/tbox/xmake.lua:26 -> build/macosx/x86_64/release -> ./src/tbox/xmake.lua:27 headerfiles: -> src/(tbox/**.h)|**/impl/**.h -> ./src/tbox/xmake.lua:30 -> src/(tbox/prefix/**/prefix.S) -> ./src/tbox/xmake.lua:31 -> build/macosx/x86_64/release/tbox.config.h -> ./src/tbox/xmake.lua:34 files: -> src/tbox/*.c -> ./src/tbox/xmake.lua:56 -> src/tbox/hash/bkdr.c -> ./src/tbox/xmake.lua:57 -> src/tbox/hash/fnv32.c -> ./src/tbox/xmake.lua:57 compiler (cc): /usr/bin/xcrun -sdk macosx clang -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk linker (ar): /usr/bin/xcrun -sdk macosx ar -> -cr compflags (cc): -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -Wall -Werror -Oz -std=c99 -Isrc -Ibuild/macosx/x86_64/release -D__tb_small__ -D__tb_prefix__=\"tbox\" -D_GNU_SOURCE=1 -framework CoreFoundation -framework CoreServices -Wno-error=deprecated-declarations -fno-strict-aliasing -Wno-error=expansion-to-defined -fno-stack-protector linkflags (ar): -> -cr ``` ### Improve package download configuration If there are packages whose url downloads require specific http headers to be set to authenticate them before they can be downloaded, this policy can be specified. This is often used for the maintenance of private repository packages within some companies. ```lua package("xxx") set_policy("package.download.http_headers", "TEST1: foo", "TEST2: bar") ``` We can also set the http headers for the specified urls: \`\`\` ```lua package("zlib") add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", { http_headers = {"TEST1: foo", "TEST2: bar"} }) ``` ### Improve dlang toolchain support In previous versions, Xmake only provided a toolchain for dlang, which automatically looked up dmd, ldc2, gdc to adapt to the dlang compiler that was available to compile the project. However, this approach does not allow the user more flexibility in selecting a specific compiler, and if both dmd and ldc2 are installed, Xmake will always use dmd as the compiler for dlang. Therefore, in this new version, xmake provides three separate toolchains to select the required dlang compiler. For example, you can quickly switch to the ldc2 compiler to compile your project by running the following command ```bash $ xmake f --toolchain=ldc $ xmake ``` In addition to the ldc toolchain, two other toolchains, dmd, and gdc, can be used separately. And we have also improved the configuration of the dmd/ldc2 build optimisation options to make the production dlang binaries even smaller and faster. ### Support for external working directory configuration #### The default build directory mode Xmake currently provides a build directory model that is a built-in build directory, which means that if we run the xmake command in the root of the current project, the build directory is automatically generated and .xmake is used to store some configuration cache. ``` - projectdir (workdir) - build (generated) - .xmake (generated) - src - xmake.lua ``` ```bash $ cd projectdir $ xmake ``` Of course, we can configure the build directory with `xmake f -o . /build` to configure the build directory, but the .xmake directory will still be in the project source directory. ```bash $ cd projectdir $ xmake f -o ... /build ``` This may not be to the liking of some users who like their complete code directories to remain intact and clean. #### The new external build directory mode Therefore, with this new version, Xmake offers an alternative way of configuring build directories, namely external directory builds (similar to CMake). For example, we would like to use a directory structure like the following to build a project, always keeping the source directory clean. ``` - workdir - build (generated) - .xmake (generated) - projectdir - projectdir - xmake.lua ``` We just need to go into the working directory where we need to store the build/.xmake directory and then use the \`\`xmake f -P \[projectdir]\` configuration command to specify the source root directory. ```bash $ cd workdir $ xmake f -P ... /projectdir $ xmake ``` Once the configuration is complete, the source code root is completely remembered and there is no need to set it up again for any subsequent build commands. For example, the commands to build, rebuild, run or install are exactly the same as before and the user will not feel any difference. ```bash $ xmake $ xmake run $ xmake --rebuild $ xmake clean $ xmake install ``` We can also use the `-o/--buildir` argument to set the build directory separately to another location, for example to the following structure. ``` - build (generated) - workdir - .xmake (generated) - projectdir - src - xmake.lua ``` ```bash $ cd workdir $ xmake f -P ... /projectdir -o ... /build ``` ## Changelog ### New features * Add Haiku support * [#3326](https://github.com/xmake-io/xmake/issues/3326): Add `xmake check` to check project code (clang-tidy) and configuration * [#3332](https://github.com/xmake-io/xmake/pull/3332): add custom http headers when downloading packages ### Changes * [#3318](https://github.com/xmake-io/xmake/pull/3318): Improve dlang toolchains * [#2591](https://github.com/xmake-io/xmake/issues/2591): Improve target analysis * [#3342](https://github.com/xmake-io/xmake/issues/3342): Improve to configure working and build directories * [#3373](https://github.com/xmake-io/xmake/issues/3373): Improve std modules support for clang-17 * Improve to strip/optimization for dmd/ldc2 ### Bugs fixed * [#3317](https://github.com/xmake-io/xmake/pull/3317): Fix languages for qt project. * [#3321](https://github.com/xmake-io/xmake/issues/3321): Fix dependfile when generating configiles * [#3296](https://github.com/xmake-io/xmake/issues/3296): Fix build error on macOS arm64 --- --- url: /zh/posts/xmake-update-v2.7.7.md --- ## 新特性介绍 ### 支持 Haiku 系统 Xmake 现在已经完全可以在 [Haiku 系统](https://www.haiku-os.org/) 上运行,并且我们对 Xmake 新增了一个 haiku 编译平台,用于在 Haiku 系统上进行代码编译。 效果如下: ### 改进 C++20 Modules 支持 最新构建的 clang-17 对 C++20 Modules 做了不少改进,因此我们在 Xmake 中也针对性地对其进行了更好的适配,并且修复了一些 std modules 相关的问题。 关于 C++ Modules 的完整工程例子,可以看下 [C++ Modules Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/c%2B%2B/modules)。 现在也有一些实际的 C++ Modules 项目已经使用了 Xmake 来构建,例如: * [async\_simple](https://github.com/alibaba/async_simple) * [StormKit](https://github.com/TapzCrew/StormKit) ### 改进 API 检测 先前的版本,对 xmake.lua 的配置 API 的传参有效性检测比较弱,仅仅针对 `add_includedirs`, `add_files` 等几个少数的 API 做了检测。 而新版中,我们新增了一个 `xmake check` 专门用于检测 API 和代码的插件,可以更好地对用户的配置进行检测,避免用户由于不熟悉 Xmake 导致各种配置值设置不对的问题。 另外,除了手动运行 `xmake check` 命令来触发检测,Xmake 在编译中,编译失败等各个阶段, 也会及时地自动触发一些常规 API 和配置的检测,毕竟不是所有用户都知道 `xmake check` 这个命令的存在。 #### 默认检测所有 API ```lua set_lanuages("c91") -- typo ``` ```console $ xmake check ./xmake.lua:15: warning: unknown language value 'c91', it may be 'c90' 0 notes, 1 warnings, 0 errors ``` 默认也可以指定检测特定组: ```console $ xmake check api $ xmake check api.target ``` #### 显示详细输出 这会额外提供 note 级别的检测信息。 ```console $ xmake check -v ./xmake.lua:15: warning: unknown language value 'cxx91', it may be 'cxx98' ./src/tbox/xmake.lua:43: note: unknown package value 'mbedtls' ./src/tbox/xmake.lua:43: note: unknown package value 'polarssl' ./src/tbox/xmake.lua:43: note: unknown package value 'openssl' ./src/tbox/xmake.lua:43: note: unknown package value 'pcre2' ./src/tbox/xmake.lua:43: note: unknown package value 'pcre' ./src/tbox/xmake.lua:43: note: unknown package value 'zlib' ./src/tbox/xmake.lua:43: note: unknown package value 'mysql' ./src/tbox/xmake.lua:43: note: unknown package value 'sqlite3' 8 notes, 1 warnings, 0 errors ``` #### 检测指定的 API ```console $ xmake check api.target.languages ./xmake.lua:15: warning: unknown language value 'cxx91', it may be 'cxx98' 0 notes, 1 warnings, 0 errors ``` #### 检测编译 flags ```console $ xmake check ./xmake.lua:10: warning: clang: unknown c compiler flag '-Ox' 0 notes, 1 warnings, 0 errors ``` #### 检测 includedirs 除了 includedirs,还有 linkdirs 等路径都会去检测。 ```console $ xmake check ./xmake.lua:11: warning: includedir 'xxx' not found 0 notes, 1 warnings, 0 errors ``` ### 支持检测工程代码(clang-tidy) #### 显示 clang-tidy 检测列表 ```console $ xmake check clang.tidy --list Enabled checks: clang-analyzer-apiModeling.StdCLibraryFunctions clang-analyzer-apiModeling.TrustNonnull clang-analyzer-apiModeling.google.GTest clang-analyzer-apiModeling.llvm.CastValue clang-analyzer-apiModeling.llvm.ReturnValue ... ``` #### 检测所有 targets 中的源码 ```console $ xmake check clang.tidy 1 error generated. Error while processing /private/tmp/test2/src/main.cpp. /tmp/test2/src/main.cpp:1:10: error: 'iostr' file not found [clang-diagnostic-error] #include ^~~~~~~ Found compiler error(s). error: execv(/usr/local/opt/llvm/bin/clang-tidy -p compile_commands.json /private/tmp/test2/src /main.cpp) failed(1) ``` #### 指定检测类型 我们可以在 `--check=` 中指定需要检测的类型,具体用法可以参考 `clang-tidy` 的 `--check=` 参数,完全一致的。 ```console $ xmake check clang.tidy --checks="*" 6 warnings and 1 error generated. Error while processing /private/tmp/test2/src/main.cpp. /tmp/test2/src/main.cpp:1:10: error: 'iostr' file not found [clang-diagnostic-error] #include ^~~~~~~ /tmp/test2/src/main.cpp:3:1: warning: do not use namespace using-directives; use using-declarat ions instead [google-build-using-namespace] using namespace std; ^ /tmp/test2/src/main.cpp:3:17: warning: declaration must be declared within the '__llvm_libc' na mespace [llvmlibc-implementation-in-namespace] using namespace std; ^ /tmp/test2/src/main.cpp:5:5: warning: declaration must be declared within the '__llvm_libc' nam espace [llvmlibc-implementation-in-namespace] int main(int argc, char **argv) { ^ /tmp/test2/src/main.cpp:5:5: warning: use a trailing return type for this function [modernize-u se-trailing-return-type] int main(int argc, char **argv) { ~~~ ^ auto -> int /tmp/test2/src/main.cpp:5:14: warning: parameter 'argc' is unused [misc-unused-parameters] int main(int argc, char **argv) { ^~~~ /*argc*/ /tmp/test2/src/main.cpp:5:27: warning: parameter 'argv' is unused [misc-unused-parameters] int main(int argc, char **argv) { ^~~~ /*argv*/ Found compiler error(s). error: execv(/usr/local/opt/llvm/bin/clang-tidy --checks=* -p compile_commands.json /private/tm p/test2/src/main.cpp) failed(1) ``` #### 检测指定 target 的代码 ```console $ xmake check clang.tidy [targetname] ``` #### 检测给定的源文件列表 ```console $ xmake check clang.tidy -f src/main.c $ xmake check clang.tidy -f 'src/*.c:src/**.cpp' ``` #### 设置 .clang-tidy 配置文件 ```console $ xmake check clang.tidy --configfile=/tmp/.clang-tidy ``` #### 创建 .clang-tidy 配置文件 ```console $ xmake check clang.tidy --checks="*" --create $ cat .clang-tidy --- Checks: 'clang-diagnostic-*,clang-analyzer-*,*' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: none User: ruki CheckOptions: - key: readability-suspicious-call-argument.PrefixSimilarAbove value: '30' - key: cppcoreguidelines-no-malloc.Reallocations value: '::realloc' ``` ### 改进 target 配置来源分析 我们改进了 `xmake show -t target` 命令对 target 信息的展示,新增了配置来源分析,并且精简了一些相对冗余的信息。 我们可以用它更好地排查定位自己配置的一些 flags 实际来自那一行配置。 显示效果如下: ```bash $ xmake show -t tbox The information of target(tbox): at: /Users/ruki/projects/personal/tbox/src/tbox/xmake.lua kind: static targetfile: build/macosx/x86_64/release/libtbox.a rules: -> mode.release -> ./xmake.lua:26 -> mode.debug -> ./xmake.lua:26 -> utils.install.cmake_importfiles -> ./src/tbox/xmake.lua:15 -> utils.install.pkgconfig_importfiles -> ./src/tbox/xmake.lua:16 options: -> object -> ./src/tbox/xmake.lua:53 -> charset -> ./src/tbox/xmake.lua:53 -> database -> ./src/tbox/xmake.lua:53 packages: -> mysql -> ./src/tbox/xmake.lua:43 -> sqlite3 -> ./src/tbox/xmake.lua:43 links: -> pthread -> option(__keyword_thread_local) -> @programdir/includes/check_csnippets.lua:100 syslinks: -> pthread -> ./xmake.lua:71 -> dl -> ./xmake.lua:71 -> m -> ./xmake.lua:71 -> c -> ./xmake.lua:71 defines: -> __tb_small__ -> ./xmake.lua:42 -> __tb_prefix__="tbox" -> ./src/tbox/xmake.lua:19 -> _GNU_SOURCE=1 -> option(__systemv_semget) -> @programdir/includes/check_cfuncs.lua:104 cxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:22 -> -fno-strict-aliasing -> ./xmake.lua:22 -> -Wno-error=expansion-to-defined -> ./xmake.lua:22 -> -fno-stack-protector -> ./xmake.lua:51 frameworks: -> CoreFoundation -> ./src/tbox/xmake.lua:38 -> CoreServices -> ./src/tbox/xmake.lua:38 mxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:23 -> -fno-strict-aliasing -> ./xmake.lua:23 -> -Wno-error=expansion-to-defined -> ./xmake.lua:23 includedirs: -> src -> ./src/tbox/xmake.lua:26 -> build/macosx/x86_64/release -> ./src/tbox/xmake.lua:27 headerfiles: -> src/(tbox/**.h)|**/impl/**.h -> ./src/tbox/xmake.lua:30 -> src/(tbox/prefix/**/prefix.S) -> ./src/tbox/xmake.lua:31 -> build/macosx/x86_64/release/tbox.config.h -> ./src/tbox/xmake.lua:34 files: -> src/tbox/*.c -> ./src/tbox/xmake.lua:56 -> src/tbox/hash/bkdr.c -> ./src/tbox/xmake.lua:57 -> src/tbox/hash/fnv32.c -> ./src/tbox/xmake.lua:57 compiler (cc): /usr/bin/xcrun -sdk macosx clang -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk linker (ar): /usr/bin/xcrun -sdk macosx ar -> -cr compflags (cc): -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -Wall -Werror -Oz -std=c99 -Isrc -Ibuild/macosx/x86_64/release -D__tb_small__ -D__tb_prefix__=\"tbox\" -D_GNU_SOURCE=1 -framework CoreFoundation -framework CoreServices -Wno-error=deprecated-declarations -fno-strict-aliasing -Wno-error=expansion-to-defined -fno-stack-protector linkflags (ar): -> -cr ``` ### 改进包的下载配置 如果有些包的 url 下载,需要设置特定 http headers 去鉴权后,才能通过下载,可以通过这个策略来指定。 这通常用于一些公司内部的私有仓库包的维护。 ```lua package("xxx") set_policy("package.download.http_headers", "TEST1: foo", "TEST2: bar") ``` 我们也可以设置指定的 urls 的 http headers: ```lua package("zlib") add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", { http_headers = {"TEST1: foo", "TEST2: bar"} }) ``` ### 改进 dlang 工具链支持 先前的版本,Xmake 仅仅提供了 dlang 这一个工具链,内部会自动取查找 dmd, ldc2, gdc 来适配选择能够获取到的 dlang 编译器去编译项目。 但是这种方式,对用户而言,无法更加灵活地去选择指定的编译器,如果同时安装了 dmd, ldc2,那么 Xmake 总是会优先使用 dmd 作为 dlang 的编译器。 因此,新版本中,xmake 额外提供了三个独立的工具链可以单独选择需要的 dlang 编译器。 比如,运行下面的命令,就可以快速切换到 ldc2 编译器去编译项目。 ```bash $ xmake f --toolchain=ldc $ xmake ``` 除了 ldc 工具链,还有 dmd, gdc 这两个工具链可以单独选择使用。 并且,我们还改进了 dmd/ldc2 的编译优化选项配置,使得生产的 dlang 二进制程序更加的小而快。 ### 支持外置构建目录配置 #### 现有的内置构建目录模式 Xmake 目前提供的构建目录模式,属于内置构建目录,也就是如果我们在当前工程根目录下运行 xmake 命令,就会自动生成 build 目录,并且 .xmake 用于存放一些配置缓存。 ``` - projectdir (workdir) - build (generated) - .xmake (generated) - src - xmake.lua ``` ```bash $ cd projectdir $ xmake ``` 当然,我们可以通过 `xmake f -o ../build` 去配置修改构建目录,但是 .xmake 目录还是在工程源码目录下。 ```bash $ cd projectdir $ xmake f -o ../build ``` 这对于一些喜欢完全代码目录保持完整干净的用户而言,可能并不喜欢这种方式。 #### 新的外置构建目录模式 因此,新版本中,Xmake 提供了另外一种构建目录配置方式,也就是外置目录构建(类似 CMake)。 比如,我们想使用下面这种目录结构去构建项目,总是保持源码目录干净。 ``` - workdir - build (generated) - .xmake (generated) - projectdir - src - xmake.lua ``` 我们只需要进入需要存储 build/.xmake 目录的工作目录下,然后使用 `xmake f -P [projectdir]` 配置命令去指定源码根目录即可。 ```bash $ cd workdir $ xmake f -P ../projectdir $ xmake ``` 配置完成后,源码根目录就被完全记住了,后面的任何构建命令,都不需要再去设置它,就跟之前一样使用就行。 比如,构建,重建,运行或者安装等命令,跟之前的使用完全一致,用户感觉不到任何差异。 ```bash $ xmake $ xmake run $ xmake --rebuild $ xmake clean $ xmake install ``` 我们同样可以使用 `-o/--buildir` 参数去单独设置构建目录到其他地方,例如设置成下面这个结构。 ``` - build (generated) - workdir - .xmake (generated) - projectdir - src - xmake.lua ``` ```bash $ cd workdir $ xmake f -P ../projectdir -o ../build ``` ## 更新内容 ### 新特性 * 添加 Haiku 支持 * [#3326](https://github.com/xmake-io/xmake/issues/3326): 添加 `xmake check` 去检测工程代码 (clang-tidy) 和 API 参数配置 * [#3332](https://github.com/xmake-io/xmake/pull/3332): 在包中配置添加自定义 http headers ### 改进 * [#3318](https://github.com/xmake-io/xmake/pull/3318): 改进 dlang 工具链 * [#2591](https://github.com/xmake-io/xmake/issues/2591): 改进 target 配置来源分析 * 为 dmd/ldc2 改进 strip/optimization * [#3342](https://github.com/xmake-io/xmake/issues/3342): 改进配置构建目录,支持外置目录构建,保持源码目录更加干净 * [#3373](https://github.com/xmake-io/xmake/issues/3373): 为 clang-17 改进 std 模块支持 ### Bugs 修复 * [#3317](https://github.com/xmake-io/xmake/pull/3317): 针对 Qt 工程,修复 lanuages 设置 * [#3321](https://github.com/xmake-io/xmake/issues/3321): 修复隔天 configfiles 重新生成导致重编问题 * [#3296](https://github.com/xmake-io/xmake/issues/3296): 修复 macOS arm64 上构建失败 --- --- url: /posts/xmake-update-v2.7.8.md --- ## Introduction of new features ### Quickly switch temporary virtual environments Xmake has long supported the virtual environment management of packages, and can switch between different package environments through configuration files. We can customize some package configurations by adding the xmake.lua file in the current directory, and then enter a specific package virtual environment. ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```console $ xrepo env shell > python --version > luajit --version ``` You can also switch environments by importing custom environment configuration files: ```console $ xrepo env --add /tmp/base.lua $ xrepo env -b base shell ``` In the new version, we have made further improvements, allowing Xrepo to temporarily specify the list of environment packages that need to be bound directly on the command line to achieve fast switching without any configuration. And it supports specifying multiple package environments at the same time. For example, we want to enter an environment with python 3.0, luajit and cmake, just execute: ```console $ xrepo env -b "python 3.x,luajit,cmake" shell [python, luajit, cmake] $ python --version Python 3.10.6 [python, luajit, cmake] $ cmake --version cmake version 3.25.3 ``` Xmake will automatically install the relevant dependencies, and then open a new shell environment. There is also a prompt prompt on the left side of the terminal in the new environment. If we want to exit the current environment, we only need to execute ```console [python, luajit, cmake] $ xrepo env quit $ ``` ### Improve code feature detection A series of detection interfaces such as has\_cfuncs/check\_cxxsnippets have been provided in option, and there are corresponding auxiliary APIs to help detection. For related documents, please refer to: [helper detection interface](https://xmake.io/api/description/helper-interfaces.html). However, the current detection interface provided by option is only for the global platform tool chain, and it is impossible to perform targeted detection according to each specific target configuration. Because the target itself may also have dependent packages, different tool chains, compilation macros and other differences, the detection results will also have some differences. Therefore, if users want more flexible and fine-grained detection of the compilation characteristics of each target target, they can use the target target instance interface provided by the new version. * target: has\_cfuncs * target: has\_cxxfuncs * target:has\_ctypes * target:has\_cxxtypes * target: has\_cincludes * target:has\_cxxincludes * target:has\_cflags * target:has\_cxxflags * target:has\_features * target: check\_csnippets * target: check\_cxxsnippets Here, only for some of the more commonly used interfaces, a little introduction to the usage. #### target:has\_cfuncs * Check whether the target compilation configuration can obtain the given C function This should be used in `on_config`, for example, it can be used to determine whether the current target can obtain some function interfaces of the zlib dependent package, and then automatically define `HAVE_INFLATE`: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function(target) if target:has_cfuncs("inflate", {includes = "zlib.h"}) then target:add("defines", "HAVE_INFLATE") end end) ``` Although option also provides similar detection functions, the detection of option uses the global platform tool chain, which cannot be accompanied by some compilation configurations related to target. It is also impossible to set different compilation toolchains according to the target to adapt the detection, and it is impossible to detect some interfaces in the package. If we only want a coarse-grained detection function interface, and the target does not additionally set different tool chains, then the detection function provided by option is sufficient. If you want more fine-grained control over detection, you can use the detection features provided by the target instance interface. #### target:has\_cxxfuncs * Check whether the target compilation configuration can obtain the given C++ function The usage is similar to [target:has\_cfuncs](https://xmake.io), except that it is mainly used to detect C++ functions. However, while detecting functions, we can also additionally configure std languages to assist detection. ``` target:has_cxxfuncs("foo", {includes = "foo.h", configs = {languages = "cxx17"}}) ``` #### target:has\_ctypes * Check whether the target compilation configuration can obtain the given C type This should be used in `on_config` like this: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function(target) if target:has_ctypes("z_stream", {includes = "zlib.h"}) then target:add("defines", "HAVE_ZSTEAM_T") end end) ``` #### target:has\_cflags * Check whether the target compilation configuration can obtain the given C compilation flags ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) if target:has_cxxflags("-fPIC") then target:add("defines", "HAS_PIC") end end) ``` #### target:has\_cincludes * Check whether the target compilation configuration can obtain the given C header file This should be used in `on_config`, for example, it can be used to determine whether the current target can obtain the zlib.h header file of the zlib dependency package, and then automatically define `HAVE_INFLATE`: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function(target) if target:has_cincludes("zlib.h") then target:add("defines", "HAVE_ZLIB_H") end end) ``` #### target:check\_cxxsnippets * Detect if a given piece of C++ code can be compiled and linked This should be used in `on_config` like this: ```lua add_requires("libtins") target("test") set_kind("binary") add_files("src/*.cpp") add_packages("libtins") on_config(function(target) local has_snippet = target:check_cxxsnippets({test = [[ #include using namespace Tins; void test() { std::string name = NetworkInterface::default_interface().name(); printf("%s\n", name.c_str()); } ]]}, {configs = {languages = "c++11"}, includes = {"tins/tins.h"}})) if has_snippet then target:add("defines", "HAS_XXX") end end) ``` By default, it only checks whether the compilation link is passed. If you want to try the runtime check, you can set `tryrun = true`. ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) local has_int_4 = target:check_cxxsnippets({test = [[ return (sizeof(int) == 4)? 0 : -1; ]]}, {configs = {languages = "c++11"}, tryrun = true})) if has_int_4 then target:add("defines", "HAS_INT4") end end) ``` We can also continue to capture the running output of the detection by setting `output = true`, and add a custom `main` entry to achieve a complete test code, not just a code snippet. ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) local int_size = target:check_cxxsnippets({test = [[ #include int main(int argc, char** argv) { printf("%d", sizeof(int)); return 0; return 0; } ]]}, {configs = {languages = "c++11"}, tryrun = true,output = true})) end) ``` #### target:has\_features * Detect if specified C/C++ compilation feature It is faster than using `check_cxxsnippets`, because it only performs preprocessing once to check all compiler features, instead of calling the compiler every time to try to compile. ``` target("test") set_kind("binary") add_files("src/*.cpp") on_config(function(target) if target:has_features("c_static_assert") then target:add("defines", "HAS_STATIC_ASSERT") end if target:has_features("cxx_constexpr") then target:add("defines", "HAS_CXX_CONSTEXPR") end end) ``` ### Optimize compilation performance The build cache acceleration of Xmake is similar to ccache, which uses the preprocessor to calculate the hash and cache the compiled object files to achieve acceleration. It has a very obvious speed-up effect on linux/mac. And because the preprocessor of msvc is very slow, it may also be that the starting process is heavier than that under linux/mac. After the build cache is enabled, the overall compilation efficiency of using msvc on windows is much slower. Trying to use a third-party ccache to test and compare, the same problem, so I temporarily disabled the build cache for msvc by default, so that the overall build speed returned to normal levels. ### clang-tidy autofix In the last version, we added support for clang-tidy, and you can check the code through `xmake check clang.tidy`. In this version, we continue to improve it and add the `--fix` parameter, which allows clang-tidy to automatically fix the detected problem code. ```console $ xmake check clang.tidy --fix $ xmake check clang.tidy --fix_errors $ xmake check clang.tidy --fix_notes ``` ### Swig/Java module build support Additionally, other users have helped contribute build support for Swig/Java modules. ``` add_rules("mode. release", "mode. debug") target("example") set_kind('shared') --set moduletype to java add_rules("swig.c", {moduletype = "java"}) -- use swigflags to provider package name and output path of java files add_files("src/example.i", {swigflags = { "-package", "com. example", "-outdir", "build/java/com/example/" }}) add_files("src/example.c") before_build(function() -- ensure output path exists before running swig os.mkdir("build/java/com/example/") end) ``` For a complete example, see: [Swig/Java Example](https://github.com/xmake-io/xmake/tree/master/tests/projects/swig/java_c) ## Changelog ### New features * [#3518](https://github.com/xmake-io/xmake/issues/3518): Profile compile and link performance * [#3522](https://github.com/xmake-io/xmake/issues/3522): Add has\_cflags, has\_xxx for target * [#3537](https://github.com/xmake-io/xmake/issues/3537): Add --fix for clang.tidy checker ### Changes * [#3433](https://github.com/xmake-io/xmake/issues/3433): Improve to build Qt project on msys2/mingw64 and wasm * [#3419](https://github.com/xmake-io/xmake/issues/3419): Support fish shell envirnoment * [#3455](https://github.com/xmake-io/xmake/issues/3455): Dlang incremental build support * [#3498](https://github.com/xmake-io/xmake/issues/3498): Improve to bind package virtual envirnoments * [#3504](https://github.com/xmake-io/xmake/pull/3504): Add swig java support * [#3508](https://github.com/xmake-io/xmake/issues/3508): Improve trybuild/cmake to support for switching toolchain * disable build cache for msvc, because msvc's preprocessor is too slow. ### Bugs fixed * [#3436](https://github.com/xmake-io/xmake/issues/3436): Fix complete and menuconf * [#3463](https://github.com/xmake-io/xmake/issues/3463): Fix c++modules cache issue * [#3545](https://github.com/xmake-io/xmake/issues/3545): Fix parsedeps for armcc --- --- url: /zh/posts/xmake-update-v2.7.8.md --- ## 新特性介绍 ### 快速切换临时虚拟环境 Xmake 很早就支持了包的虚拟环境管理,可以通过配置文件的方式,实现不同包环境之间的切换。 我们可以通过在当前目录下,添加 xmake.lua 文件,定制化一些包配置,然后进入特定的包虚拟环境。 ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```console $ xrepo env shell > python --version > luajit --version ``` 也可以通过导入自定义环境配置文件,来切换环境: ```console $ xrepo env --add /tmp/base.lua $ xrepo env -b base shell ``` 而在新版本中,我们进一步做了改进,让 Xrepo 能够直接在命令行临时指定需要绑定的环境包列表,实现快速切换,无需任何配置。 并且支持同时指定多个包环境。 例如,我们想进入一个带有 python 3.0, luajit 和 cmake 的环境,只需要执行: ```console $ xrepo env -b "python 3.x,luajit,cmake" shell [python,luajit,cmake] $ python --version Python 3.10.6 [python,luajit,cmake] $ cmake --version cmake version 3.25.3 ``` Xmake 会自动安装相关依赖,然后开启一个新的 shell 环境,新环境终端左边也有 prompt 提示。 如果我们想退出当前环境,仅仅需要执行 ```console [python,luajit,cmake] $ xrepo env quit $ ``` ### 改进代码特性检测 has\_cfuncs/check\_cxxsnippets 等系列检测接口,在 option 中已经有提供,并且有对应的辅助 API 来帮助检测。 相关文档可以参考:[辅助检测接口](https://xmake.io/zh/)。 但是目前 option 提供的检测接口仅仅针对全局平台工具链,无法根据每个特定的 target 配置在针对性做一些检测。 因为 target 本身可能还会附带依赖包,不同的工具链,编译宏等差异性,检测结果也会有一些差异。 因此,如果用户想要更加灵活细粒度的检测每个 target 目标的编译特性,可以通过新版本提供的 target 目标实例接口。 * target:has\_cfuncs * target:has\_cxxfuncs * target:has\_ctypes * target:has\_cxxtypes * target:has\_cincludes * target:has\_cxxincludes * target:has\_cflags * target:has\_cxxflags * target:has\_features * target:check\_csnippets * target:check\_cxxsnippets 这里,仅仅针对其中一些比较常用的接口,稍微展开介绍下使用方式。 #### target:has\_cfuncs * 检测目标编译配置能否获取给定的 C 函数 这应该在 `on_config` 中使用,比如可以用它来判断当前目标能否获取到 zlib 依赖包的一些函数接口,然后自动定义 `HAVE_INFLATE`: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function (target) if target:has_cfuncs("inflate", {includes = "zlib.h"}) then target:add("defines", "HAVE_INFLATE") end end) ``` 尽管 option 也提供了类似的检测功能,但 option 的检测使用的是全局的平台工具链,它无法附带上 target 相关的一些编译配置, 也无法根据 target 设置不同编译工具链来适配检测,并且无法检测包里面的一些接口。 如果我们仅仅是想粗粒度的检测函数接口,并且 target 没有额外设置不同的工具链,那么 option 提供的检测功能已经足够使用了。 如果想要更细粒度控制检测,可以使用 target 实例接口提供的检测特性。 #### target:has\_cxxfuncs * 检测目标编译配置能否获取给定的 C++ 函数 用法跟 [target:has\_cfuncs](https://xmake.io/zh/) 类似,只是这里主要用于检测 C++ 的函数。 不过,在检测函数的同时,我们还可以额外配置 std languages,来辅助检测。 ``` target:has_cxxfuncs("foo", {includes = "foo.h", configs = {languages = "cxx17"}}) ``` #### target:has\_ctypes * 检测目标编译配置能否获取给定的 C 类型 这应该在 `on_config` 中使用,如下所示: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function (target) if target:has_ctypes("z_stream", {includes = "zlib.h"}) then target:add("defines", "HAVE_ZSTEAM_T") end end) ``` #### target:has\_cflags * 检测目标编译配置能否获取给定的 C 编译 flags ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) if target:has_cxxflags("-fPIC") then target:add("defines", "HAS_PIC") end end) ``` #### target:has\_cincludes * 检测目标编译配置能否获取给定的 C 头文件 这应该在 `on_config` 中使用,比如可以用它来判断当前目标能否获取到 zlib 依赖包的 zlib.h 头文件,然后自动定义 `HAVE_INFLATE`: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function (target) if target:has_cincludes("zlib.h") then target:add("defines", "HAVE_ZLIB_H") end end) ``` #### target:check\_cxxsnippets * 检测是否可以编译和链接给定的 C++ 代码片段 这应该在 `on_config` 中使用,如下所示: ```lua add_requires("libtins") target("test") set_kind("binary") add_files("src/*.cpp") add_packages("libtins") on_config(function (target) local has_snippet = target:check_cxxsnippets({test = [[ #include using namespace Tins; void test() { std::string name = NetworkInterface::default_interface().name(); printf("%s\n", name.c_str()); } ]]}, {configs = {languages = "c++11"}, includes = {"tins/tins.h"}})) if has_snippet then target:add("defines", "HAS_XXX") end end) ``` 默认仅仅检测编译链接是否通过,如果想要尝试运行时检测,可以再设置 `tryrun = true`。 ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) local has_int_4 = target:check_cxxsnippets({test = [[ return (sizeof(int) == 4)? 0 : -1; ]]}, {configs = {languages = "c++11"}, tryrun = true})) if has_int_4 then target:add("defines", "HAS_INT4") end end) ``` 我们也可以继续通过设置 `output = true` 来捕获检测的运行输出,并且加上自定义的 `main` 入口,实现完整的测试代码,而不仅仅是代码片段。 ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) local int_size = target:check_cxxsnippets({test = [[ #include int main(int argc, char** argv) { printf("%d", sizeof(int)); return 0; return 0; } ]]}, {configs = {languages = "c++11"}, tryrun = true, output = true})) end) ``` #### target:has\_features * 检测是否指定的 C/C++ 编译特性 它相比使用 `check_cxxsnippets` 来检测,会更加快一些,因为它仅仅执行一次预处理就能检测所有的编译器特性,而不是每次都去调用编译器尝试编译。 ``` target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) if target:has_features("c_static_assert") then target:add("defines", "HAS_STATIC_ASSERT") end if target:has_features("cxx_constexpr") then target:add("defines", "HAS_CXX_CONSTEXPR") end end) ``` ### 优化编译性能 Xmake 的 build cache 加速类似 ccache,采用预处理器计算 hash 后缓存编译对象文件来实现加速,它在 linux/mac 上提速效果非常明显。 而由于 msvc 的预处理器很慢,也可能是起进程相比 linux/mac 下更重,导致开启 build cache 后,windows 上使用 msvc 的整体编译效率反而慢了非常多。 尝试使用第三方的 ccache 来测试对比,也是一样的问题,因此我暂时针对 msvc 默认禁用了 build cache,使得整体构建速度恢复到正常水平。 ### clang-tidy 自动修复 上个版本,我们新增了对 clang-tidy 支持,可以通过 `xmake check clang.tidy` 来检测代码。 而在这个版本中,我们继续对它做了改进,新增了 `--fix` 参数,可以让 clang-tidy 去自动修复检测出来的问题代码。 ```console $ xmake check clang.tidy --fix $ xmake check clang.tidy --fix_errors $ xmake check clang.tidy --fix_notes ``` ### Swig/Java 模块构建支持 另外,其他用户也帮忙贡献了 Swig/Java 模块的构建支持。 ``` add_rules("mode.release", "mode.debug") target("example") set_kind('shared') -- set moduletype to java add_rules("swig.c", {moduletype = "java"}) -- use swigflags to provider package name and output path of java files add_files("src/example.i", {swigflags = { "-package", "com.example", "-outdir", "build/java/com/example/" }}) add_files("src/example.c") before_build(function() -- ensure output path exists before running swig os.mkdir("build/java/com/example/") end) ``` 完整例子见:[Swig/Java Example](https://github.com/xmake-io/xmake/tree/master/tests/projects/swig/java_c) ### 开源之夏 2023 今年 Xmake 社区继续参加了开源之夏 2023 活动,它是由中科院软件所“开源软件供应链点亮计划”发起并长期支持的一项暑期开源活动 旨在鼓励在校学生积极参与开源软件的开发维护。 如果有感兴趣的同学,欢迎报名参与 Xmake 社区发布的项目开发(具体项目待定中),相关详情进展,请关注:[Xmake 开源之夏](https://summer-ospp.ac.cn/org/orgdetail/090748c6-6504-4d2d-9a11-f9f3e1876f7b)。 ## 更新内容 ### 新特性 * [#3518](https://github.com/xmake-io/xmake/issues/3518): 分析编译和链接性能 * [#3522](https://github.com/xmake-io/xmake/issues/3522): 为 target 添加 has\_cflags, has\_xxx 等辅助接口 * [#3537](https://github.com/xmake-io/xmake/issues/3537): 为 clang.tidy 检测器添加 `--fix` 自动修复 ### 改进 * [#3433](https://github.com/xmake-io/xmake/issues/3433): 改进 QT 在 msys2/mingw64 和 wasm 上的构建支持 * [#3419](https://github.com/xmake-io/xmake/issues/3419): 支持 fish shell 环境 * [#3455](https://github.com/xmake-io/xmake/issues/3455): Dlang 增量编译支持 * [#3498](https://github.com/xmake-io/xmake/issues/3498): 改进绑定包虚拟环境 * [#3504](https://github.com/xmake-io/xmake/pull/3504): 添加 swig java 支持 * [#3508](https://github.com/xmake-io/xmake/issues/3508): 改进 trybuild/cmake 去支持工具链切换 * 为 msvc 禁用 build cache 加速,因为 msvc 的预处理器太慢,反而极大影响构建性能。 ### Bugs 修复 * [#3436](https://github.com/xmake-io/xmake/issues/3436): 修复自动补全和 menuconf * [#3463](https://github.com/xmake-io/xmake/issues/3463): 修复 c++modules 缓存问题 * [#3545](https://github.com/xmake-io/xmake/issues/3545): 修复 armcc 的头文件依赖解析 败 --- --- url: /posts/xmake-update-v2.8.1.md --- ## Introduction of new features ### Windows long path problem improvement Windows' long path limitation has always been a big problem. Projects that are nested too deeply may fail when reading or writing files, which affects xmake's usability and experience. Although xmake has provided various measures to avoid this problem, it still suffers from some limitations occasionally. In this release, we have improved the installer by providing an installation option that lets you selectively enable long path support. This requires administrator privileges, as it requires a registry write. ``` WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ``` Users can decide for themselves, whether they need to turn it on or not. Thanks to @A2va for the contribution. ### zypper package manager support Added support for OpenSUSE's zypper package manager, which can be automatically downloaded and installed directly from zypper, and integrates with the packages it provides. Thanks to @iphelf for his contribution. ```lua add_requires("zypper::libsfml2 2.5") ``` ### Improve msbuild package installation Some third-party packages, which are not maintained by cmake, just provide the vcproj project file, and if we make it into a package, we need to use the `tools.msbuild` module to compile and install it. But if the vs version of vcproj is very old, we need to upgrade it, otherwise the compilation will fail. So we have improved the tools.msbuild module to provide automatic vcproj upgrades by specifying the vcproj/sln files that need to be upgraded. ```lua package("test") on_install(function (package) import("package.tools.msbuild").build(package, configs, {upgrade={"wolfssl64.sln", "wolfssl.vcxproj"}})) end) ``` ### Improved protobuf support for grpc We have improved protobuf support to also support the grpc\_cpp\_plugin. ```lua add_rules("mode.debug", "mode.release") add_requires("protobuf-cpp") add_requires("grpc", {system = false}) target("test") set_kind("binary") set_languages("c++17") add_packages("protobuf-cpp") add_packages("grpc") add_rules("protobuf.cpp") add_files("src/*.cpp") add_files("src/test.proto", {proto_rootdir = "src", proto_grpc_cpp_plugin = true}) add_files("src/subdir/test2.proto", {proto_rootdir = "src"}) ``` For a full example see: \[protobuf\_grpc\_cpp\_plugin]\(https://github.com/xmake-io/xmake/blob/dev/tests/projects/c%2B%2B/protobuf\_grpc\_cpp\_plugin/xmake. lua) ### add\_links support for library paths Normally add\_links needs to be used in conjunction with add\_linkdirs in order for the linker to find library files in the specified directory. However, sometimes it is easy to find the wrong library if it is not configured correctly, or if libraries are renamed in different paths. Now add\_links can be used to set the path of library files directly to avoid implicit search. It can also be used to explicitly link so/a libraries. The following writeups are supported: ````lua add_links("foo") add_links("libfoo.a") add_links("libfoo.so") add_links("/tmp/libfoo.a") add_links("/tmp/libfoo.so") add_links("foo.lib") `` ### Objc/Objc++ header pre-compilation support In previous versions, if we used `set_pcxxheader` to set c++ header precompilation, it would also affect objc code. So if the C++/ObjC++ code is compiled mixed with pre-compiled headers, you will encounter compilation problems. ```bash Objective-C was disabled in PCH file but is currently enabled `` This is because the compilation of the precompiled header also requires the language `-x c++-header`, `-x objective-c++-header`, which cannot be mixed in the PCH file. Therefore, we added `set_pmheader` and `set_pmxxheader` interfaces to set objc/objc++ precompiled headers separately, which do not conflict with C/C++ precompiled headers. But the usage is exactly the same. ```lua target("test") set_pmxxheader("header.h") ```` For a full example see: [Objc Precompiled Header Example](https://github.com/xmake-io/xmake/tree/master/tests/projects/objc%2B%2B/precompiled_header) ### Improved Conan 2.0 support In the last release, we initially supported Conan 2.0, but we encountered a number of detailed problems. In this release, we have continued to make improvements, such as improving the vs\_runtime setting. ### Updating the lua runtime Lua has recently released version 5.4.6, and we've updated the Lua runtime built into xmake to keep up with the upstream. ## Changelog ### New features * [#3821](https://github.com/xmake-io/xmake/pull/3821): Add longpath option for windows installer * [#3828](https://github.com/xmake-io/xmake/pull/3828): Add support for zypper package manager * [#3871](https://github.com/xmake-io/xmake/issues/3871): Improve tools.msbuild to support for upgrading vsproj * [#3148](https://github.com/xmake-io/xmake/issues/3148): Support grpc for protobuf * [#3889](https://github.com/xmake-io/xmake/issues/3889): Support to add library path for add\_links * [#3912](https://github.com/orgs/xmake-io/issues/3912): Add set\_pmxxheader to support objc precompiled header * add\_links support library file path ### Changes * [#3752](https://github.com/xmake-io/xmake/issues/3752): Improve os.getenvs for windows * [#3371](https://github.com/xmake-io/xmake/issues/3371): Improve tools.cmake to support ninja generator for wasm * [#3777](https://github.com/xmake-io/xmake/issues/3777): Improve to find package from pkg-config * [#3815](https://github.com/xmake-io/xmake/pull/3815): Improve tools.xmake to pass toolchains for windows * [#3857](https://github.com/xmake-io/xmake/issues/3857): Improve to generate compile\_commands.json * [#3892](https://github.com/xmake-io/xmake/issues/3892): Improve to search packages from description * [#3916](https://github.com/xmake-io/xmake/issues/3916): Improve to build swift program, support for multiple modules * Update lua runtime to 5.4.6 ### Bugs fixed * [#3755](https://github.com/xmake-io/xmake/pull/3755): Fix find\_tool from xmake/packages * [#3787](https://github.com/xmake-io/xmake/issues/3787): Fix packages from conan 2.x * [#3839](https://github.com/orgs/xmake-io/discussions/3839): Fix vs\_runtime for conan 2.x --- --- url: /zh/posts/xmake-update-v2.8.1.md --- ## 新特性介绍 ### Windows 长路径问题改进 windows 的长路径限制一直是一个大问题,嵌套层级太深的工程,在读写文件的时候,都有可能失败,这会影响 xmake 的可用性和体验。 尽管,xmake 已经提供各种措施也避免这个问题,但是偶尔还是会受到一些限制。而在这个版本中,我们改进了安装器,提供一个安装选项,让用户选择性开启长路径支持。 这需要管理员权限,因为它需要写注册表。 ``` WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ``` 用户可以自己决定,是否需要开启它。 感谢 @A2va 的贡献。 ### zypper 包管理器支持 新增 OpenSUSE 的 zypper 包管理器支持,可以直接通过 zypper 自动下载安装,并集成它提供的包。 感谢 @iphelf 的贡献。 ```lua add_requires("zypper::libsfml2 2.5") ``` ### 改进 msbuild 包安装 一些第三方包,没有使用 cmake 维护,仅仅提供了 vcproj 的工程文件,如果我们把它做成包,需要使用 `tools.msbuild` 模块去编译安装它。 但是 vcproj 的 vs 版本如果很老,就需要升级它,否则编译会失败。 因此我们改进了 tools.msbuild 模块,提供自动升级 vcproj 的功能,只需要指定下需要升级的 vcproj/sln 文件即可。 ```lua package("test") on_install(function (package) import("package.tools.msbuild").build(package, configs, {upgrade={"wolfssl64.sln", "wolfssl.vcxproj"}}) end) ``` ### 改进 protobuf 支持 grpc 我们改进了对 protobuf 的支持,可以同时支持上 grpc\_cpp\_plugin。 ```lua add_rules("mode.debug", "mode.release") add_requires("protobuf-cpp") add_requires("grpc", {system = false}) target("test") set_kind("binary") set_languages("c++17") add_packages("protobuf-cpp") add_packages("grpc") add_rules("protobuf.cpp") add_files("src/*.cpp") add_files("src/test.proto", {proto_rootdir = "src", proto_grpc_cpp_plugin = true}) add_files("src/subdir/test2.proto", {proto_rootdir = "src"}) ``` 完整例子见:[protobuf\_grpc\_cpp\_plugin](https://github.com/xmake-io/xmake/blob/dev/tests/projects/c%2B%2B/protobuf_grpc_cpp_plugin/xmake.lua) ### add\_links 支持库路径 通常 add\_links 需要配合 add\_linkdirs 使用,才能让链接器找到指定目录下的库文件。 但是有时候配置不对,或者不同路径下库重名,就容易找错库文件。而现在 add\_links 可以支持直接设置库文件路径,避免隐式搜索。 也可以用于显式指定链接 so/a 库。 下面的几种写法都是支持的: ```lua add_links("foo") add_links("libfoo.a") add_links("libfoo.so") add_links("/tmp/libfoo.a") add_links("/tmp/libfoo.so") add_links("foo.lib") ``` ### Objc/Objc++ 头文件预编译支持 之前的版本,我们如果使用 `set_pcxxheader` 设置 c++ 头文件预编译,会同时影响 objc 代码。 因此如果 C++/ObjC++ 代码混合编译,用了预编译头,就会遇到编译问题。 ```bash Objective-C was disabled in PCH file but is currently enabled ``` 这是因为,预编译头的编译,也是需要指定语言的 `-x c++-header`, `-x objective-c++-header`,pch 文件不能混用。 因此,我们新增了 `set_pmheader` 和 `set_pmxxheader` 接口,单独设置 objc/objc++ 的预编译头文件,跟 C/C++ 预编译头互不冲突。 但用法完全一样。 ```lua target("test") set_pmxxheader("header.h") ``` 完整例子见:[Objc Precompiled Header Example](https://github.com/xmake-io/xmake/tree/master/tests/projects/objc%2B%2B/precompiled_header) ### 改进 Conan 2.0 支持 上个版本,我们初步支持了 Conan 2.0,但是还遇到了一些细节问题,这个版本我们持续做了改进,比如改进对 vs\_runtime 设置问题。 ### 更新 lua 运行时 最近 Lua 已经发布了 5.4.6 版本,我们对 xmake 中内置的 Lua 运行时也做了升级,跟上游保持同步。 ## 更新日志 ### 新特性 * [#3821](https://github.com/xmake-io/xmake/pull/3821): windows 安装器添加长路径支持选项 * [#3828](https://github.com/xmake-io/xmake/pull/3828): 添加 zypper 包管理器支持 * [#3871](https://github.com/xmake-io/xmake/issues/3871): 改进 tools.msbuild 支持对 vsproj 进行自动升级 * [#3148](https://github.com/xmake-io/xmake/issues/3148): 改进 protobuf 支持 grpc * [#3889](https://github.com/xmake-io/xmake/issues/3889): add\_links 支持库路径添加 * [#3912](https://github.com/xmake-io/xmake/issues/3912): 添加 set\_pmxxheader 去支持 objc 预编译头 * add\_links 支持库文件路径 ### 改进 * [#3752](https://github.com/xmake-io/xmake/issues/3752): 改进 windows 上 os.getenvs 的获取 * [#3371](https://github.com/xmake-io/xmake/issues/3371): 改进 tools.cmake 支持使用 ninja 去构建 wasm 包 * [#3777](https://github.com/xmake-io/xmake/issues/3777): 改进从 pkg-config 中查找包 * [#3815](https://github.com/xmake-io/xmake/pull/3815): 改进 tools.xmake 支持为 windows 平台传递工具链 * [#3857](https://github.com/xmake-io/xmake/issues/3857): 改进生成 compile\_commands.json * [#3892](https://github.com/xmake-io/xmake/issues/3892): 改进包搜索,支持从描述中找包 * [#3916](https://github.com/xmake-io/xmake/issues/3916): 改进构建 swift 程序,支持模块间符号调用 * 更新 lua 运行时到 5.4.6 ### Bugs 修复 * [#3755](https://github.com/xmake-io/xmake/pull/3755): 修复 find\_tool 从 xmake/packages 中查找程序 * [#3787](https://github.com/xmake-io/xmake/issues/3787): 修复从 conan 2.x 中使用包 * [#3839](https://github.com/orgs/xmake-io/discussions/3839): 修复 conan 2.x 包的 vs\_runtime 设置 --- --- url: /posts/xmake-update-v2.8.2.md --- ## Introduction of new features In this release, we've added a number of useful APIs, removed some interfaces that were marked as deprecated a few years ago, and improved soname support for dynamic libraries. Meanwhile, we've had some good news in the meantime: our [xmake-repo](https://github.com/xmake-io/xmake-repo) official repository has surpassed 1k packages, thanks to every contributor to Xmake, which is basically a repository of packages contributed by the community. Especially @xq114, @star-hengxing, @SirLynix contributed a lot of packages, thank you very much~. Also, the Xmake repository commits have reached 12k, and have been iterating rapidly. Here's a brief introduction to some of the major updates in the new version. ### Add soname support In this release, we have added soname version support to the `set_version` interface, which is used to control the version compatibility of the so/dylib dynamic library. You can configure the soname version suffix, and xmake will automatically generate a symbolic link to execute the specified version of the library when compiling and installing it. For example, if we configure: ```lua set_version("1.0.1", {soname = true}) ``` xmake will automatically resolve the major version of the version number as the soname version, generating the following structure: ``` └── lib ├── libfoo.1.0.1.dylib ├── libfoo.1.0.1.dylib -> libfoo.1.0.1.dylib └── libfoo.dylib -> libfoo.1.dylib ``` Of course, we can also specify soname to a specific version naming: ```lua set_version("1.0.1", {soname = "1.0"}) -> libfoo.so.1.0, libfoo.1.0.dylib set_version("1.0.1", {soname = "1"}) -> libfoo.so.1, libfoo.1.dylib set_version("1.0.1", {soname = "A"}) -> libfoo.so.A, libfoo.A.dylib set_version("1.0.1", {soname = ""}) -> libfoo.so, libfoo.dylib ``` And if soname is not set, then soname version control is not enabled by default: ```lua set_version("1.0.1") -> libfoo.so, libfoo.dylib ``` ### Improve the add\_vectorexts interface The add\_vectorexts interface is mainly used to add extended instruction optimisation options, and currently supports the following extended instruction sets: ```lua add_vectorexts("mmx") add_vectorexts("neon") add_vectorexts("avx", "avx2", "avx512") add_vectorexts("sse", "sse2", "sse3", "sse3", "sse4.2") ``` Where `avx512`, `sse4.2` are new directive configurations added to our new version, and we have also added a new `all` configuration item that can be used to turn on all extended directive optimisations as much as possible. ```lua add_vectorexts("all") ``` ### New set\_encodings interface This new interface is mainly used to set the encoding of source and target executables. By default, if we just specify the encoding, it will work for both the source and target files. ```lua -- for all source/target encodings set_encodings("utf-8") -- msvc: /utf-8 ``` It is equivalent to: ```lua set_encodings("source:utf-8", "target:utf-8") ``` And it only supports utf-8 encodings for now, but will be expanded in the future. If we just want to set the source file encoding or target file encoding individually, we can do that too. #### Set source encoding Usually this refers to the encoding of the source file of the compiled code, and we can set it like this. ```lua -- gcc/clang: -finput-charset=UTF-8, msvc: -source-charset=utf-8 set_encodings("source:utf-8") ``` #### Set the target file encoding It usually refers to the runtime output encoding of the target executable. ```lua -- gcc/clang: -fexec-charset=UTF-8, msvc: -target-charset=utf-8 set_encodings("target:utf-8") ``` ### New add\_forceincludes interface We have also added the `add_forceincludes` interface, which can be used to force the addition of `includes` headers directly in the configuration file. ```lua add_forceincludes("config.h") ``` It works like `#include `, but you don't need to add it explicitly in the source code. Also, its search path is controlled by `add_includedirs` instead of the direct config file path. ```lua add_forceincludes("config.h") add_includedirs("src") ``` By default `add_forceincludes` matches c/c++/objc, if you just want to match c++ you can do so: ```lua add_forceincludes("config.h", {sourcekinds = "cxx"}) ``` If you want to match multiple source file types at the same time, that's also possible: ```lua add_forceincludes("config.h", {sourcekinds = {"cxx", "mxx"}}) ``` For gcc it sets the `-include config.h` flag, for msvc it sets the `-FI config.h` flag. ### New add\_extrafiles interface In previous versions, if we wanted to add extra files to the project list in the vs/vsxmake project builder, we could only add them via `add_headerfiles`, but that was a bit of a Hack. Therefore, we have added the `add_extrafiles` interface specifically for configuring extra files to the project so that the user can also edit them with a quick click. These added files are not code files, they are not compiled, they are not installed, they are just a way for the user to quickly edit and access them in the generated project IDE. In the future, we may use this interface for other things as well. ```lua add_extrafiles("assets/other.txt") ``` ### sdasstm8 assembler support @lanjackg2003 helped contribute sdcc/sdasstm8 assembler support, many thanks. Related patch, [#4071](https://github.com/xmake-io/xmake/pull/4071) ### Improve Rust cross-compilation support In this new release, we've also improved our Rust project builds by adding cross-compilation support, including cross-compilation of dependent packages. ```lua set_arch("aarch64-unknown-none") add_rules("mode.release", "mode.debug") add_requires("cargo::test", {configs = { std = false, main = false, {cargo_toml = path cargo_toml = path.join(os.projectdir(), "Cargo.toml")}})) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::test") ``` For example, in the project configuration above, we can cross-compile dependent packages, as well as our own project, by globally modifying the compilation architecture with `set_arch("aarch64-unknown-none")`. If you don't have `set_arch` configured, you can also dynamically switch compilation architectures with the command `xmake f -a aarch64-unknown-none; xmake`. Of course, don't forget to run `rustup target add aarch64-unknown-none` to install the corresponding target first. For more context, see [#4049](https://github.com/xmake-io/xmake/issues/4049). ## Changelog ### New features * [#4002](https://github.com/xmake-io/xmake/issues/4002): Add soname and version support * [#1613](https://github.com/xmake-io/xmake/issues/1613): Add avx512 and sse4.2 for add\_vectorexts * [#2471](https://github.com/xmake-io/xmake/issues/2471): Add set\_encodings to set source/target encodings * [#4071](https://github.com/xmake-io/xmake/pull/4071): Support the stm8 assembler on the sdcc toolchain. * [#4101](https://github.com/xmake-io/xmake/issues/4101): Add force includes for c/c++ * [#2384](https://github.com/xmake-io/xmake/issues/2384): Add extrafiles for vs/vsxmake generator ### Changes * [#3960](https://github.com/xmake-io/xmake/issues/3960): Improve msys2/crt64 support * [#4032](https://github.com/xmake-io/xmake/pull/4032): Remove some old deprecated apis * Improve to upgrade vcproj files in tools.msbuild * Support add\_requires("xmake::xxx") package * [#4049](https://github.com/xmake-io/xmake/issues/4049): Improve rust to support cross-compilation * Improve clang modules support ### Bugs fixed * Fix exit all child processes on macOS/Linux --- --- url: /zh/posts/xmake-update-v2.8.2.md --- ## 新特性介绍 这个版本,我们新增了不少实用的 API,并且移除了一些几年前就被标记为废弃的接口,另外改进了动态库对 soname 的支持。 同时,在这期间,我们迎来了一些喜人的数据,我们的 [xmake-repo](https://github.com/xmake-io/xmake-repo) 官方仓库包的数量也突破了 1k,非常感谢 Xmake 的每位贡献者,我们的包仓库基本上都是社区贡献者贡献进来的。 尤其是 @xq114, @star-hengxing, @SirLynix 帮忙贡献了大量的包,非常感谢~ 还有,Xmake 仓库 Commits 也突破到了 12k,一直在持续快速迭代中。下面我们简单介绍下,新版本中的一些主要更新内容。 ### 增加 soname 支持 新版本中,我们对 `set_version` 接口新增了 soname 版本支持,用于控制 so/dylib 动态库的版本兼容性控制。 我们可以配置 soname 的版本后缀名称,xmake 会在编译、安装动态库的时候,自动生成符号链接,执行指定版本的动态库。 例如,如果我们配置: ```lua set_version("1.0.1", {soname = true}) ``` xmake 会自动解析版本号的 major 版本作为 soname 版本,生成的结构如下: ``` └── lib ├── libfoo.1.0.1.dylib ├── libfoo.1.dylib -> libfoo.1.0.1.dylib └── libfoo.dylib -> libfoo.1.dylib ``` 当然,我们也可以指定 soname 到特定的版本命名: ```lua set_version("1.0.1", {soname = "1.0"}) -> libfoo.so.1.0, libfoo.1.0.dylib set_version("1.0.1", {soname = "1"}) -> libfoo.so.1, libfoo.1.dylib set_version("1.0.1", {soname = "A"}) -> libfoo.so.A, libfoo.A.dylib set_version("1.0.1", {soname = ""}) -> libfoo.so, libfoo.dylib ``` 而如果没设置 soname,那么默认不开启 soname 版本兼容控制: ```lua set_version("1.0.1") -> libfoo.so, libfoo.dylib ``` ### 改进 add\_vectorexts 接口 add\_vectorexts 接口主要用于添加扩展指令优化选项,目前支持以下几种扩展指令集: ```lua add_vectorexts("mmx") add_vectorexts("neon") add_vectorexts("avx", "avx2", "avx512") add_vectorexts("sse", "sse2", "sse3", "ssse3", "sse4.2") ``` 其中,`avx512`, `sse4.2` 是我们新版本新增的指令配置,另外我们还新增了一个 `all` 配置项,可以用于尽可能的开启所有扩展指令优化。 ```lua add_vectorexts("all") ``` ### 新增 set\_encodings 接口 这个新接口主要用于设置源文件、目标执行文件的编码。 默认情况下,我们仅仅指定编码,是会同时对源文件,目标文件生效。 ```lua -- for all source/target encodings set_encodings("utf-8") -- msvc: /utf-8 ``` 它等价于: ```lua set_encodings("source:utf-8", "target:utf-8") ``` 并且,目前仅仅支持设置成 utf-8 编码,将来会不断扩展。 如果,我们仅仅想单独设置源文件编码,或者目标文件编码,也是可以的。 #### 设置源文件编码 通常指的是编译的代码源文件的编码,我们可以这么设置。 ```lua -- gcc/clang: -finput-charset=UTF-8, msvc: -source-charset=utf-8 set_encodings("source:utf-8") ``` #### 设置目标文件编码 它通常指的是目标可执行文件的运行输出编码。 ```lua -- gcc/clang: -fexec-charset=UTF-8, msvc: -target-charset=utf-8 set_encodings("target:utf-8") ``` ### 新增 add\_forceincludes 接口 我们还新增了 `add_forceincludes` 接口,用于在配置文件中直接强制添加 `includes` 头文件。 ```lua add_forceincludes("config.h") ``` 它的效果类似于 `#include `,但是不需要在源码中显式添加它了。 另外,它的搜索路径也是需要通过 `add_includedirs` 来控制,而不是直接配置文件路径。 ```lua add_forceincludes("config.h") add_includedirs("src") ``` 默认 `add_forceincludes` 匹配 c/c++/objc。如果仅仅只想匹配 c++ 可以这么配置: ```lua add_forceincludes("config.h", {sourcekinds = "cxx"}) ``` 如果想同时匹配多个源文件类型,也是可以的: ```lua add_forceincludes("config.h", {sourcekinds = {"cxx", "mxx"}}) ``` 对于 gcc,它会设置 `-include config.h` 标志,对于 msvc,它会设置 `-FI config.h` 标志。 ### 新增 add\_extrafiles 接口 在之前的版本中,如果我们要在 vs/vsxmake 工程生成器中添加一些额外的文件到工程列表中去,只能通过 `add_headerfiles` 来添加,但是这有一点 Hack。 因此,我们新增了 `add_extrafiles` 接口,专门用于配置一些额外的文件到工程中,这样,用户也可以快速点击编辑它们。 这些被添加文件不是代码文件,不会参与编译,也不会被安装,仅仅只是能够让用户方便的在生成的工程 IDE 中,快速编辑访问它们。 将来,我们也可能用此接口做更多其他的事情。 ```lua add_extrafiles("assets/other.txt") ``` ### sdasstm8 汇编器支持 @lanjackg2003 帮忙贡献了 sdcc/sdasstm8 汇编器的支持,非常感谢。 相关 patch, [#4071](https://github.com/xmake-io/xmake/pull/4071) ### 改进 Rust 交叉编译支持 新版本中,我们还对 Rust 项目构建做了改进,新增了交叉编译支持,包括对依赖包的交叉编译。 ```lua set_arch("aarch64-unknown-none") add_rules("mode.release", "mode.debug") add_requires("cargo::test", {configs = { std = false, main = false, cargo_toml = path.join(os.projectdir(), "Cargo.toml")}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::test") ``` 例如上面的项目配置,我们通过 `set_arch("aarch64-unknown-none")` 全局修改编译架构,就能对依赖包,以及自身项目进行交叉编译。 如果没有配置 `set_arch`,我们也可以通过命令 `xmake f -a aarch64-unknown-none; xmake` 来动态切换编译架构。 当然,别忘了先得执行 `rustup target add aarch64-unknown-none ` 安装对应的 target 才行。 更多上下文,见:[#4049](https://github.com/xmake-io/xmake/issues/4049) ## 更新日志 ### 新特性 * [#4002](https://github.com/xmake-io/xmake/issues/4002): 增加 soname 支持 * [#1613](https://github.com/xmake-io/xmake/issues/1613): 为 add\_vectorexts 增加 avx512 和 sse4.2 支持 * [#2471](https://github.com/xmake-io/xmake/issues/2471): 添加 set\_encodings API 去设置源文件和目标文件的编码 * [#4071](https://github.com/xmake-io/xmake/pull/4071): 支持 sdcc 的 stm8 汇编器 * [#4101](https://github.com/xmake-io/xmake/issues/4101): 为 c/c++ 添加 force includes * [#2384](https://github.com/xmake-io/xmake/issues/2384): 为 vs/vsxmake 生成器添加 add\_extrafiles 接口 ### 改进 * [#3960](https://github.com/xmake-io/xmake/issues/3960): 改进 msys2/crt64 支持 * [#4032](https://github.com/xmake-io/xmake/pull/4032): 移除一些非常老的废弃接口 * 改进 tools.msbuild 升级 vcproj 文件 * 支持 add\_requires("xmake::xxx") 包 * [#4049](https://github.com/xmake-io/xmake/issues/4049): 改进 Rust 支持交叉编译 * 改进 clang 下 c++ modules 支持 ### Bugs 修复 * 修复 macOS/Linux 上子子进程无法快速退出问题 --- --- url: /posts/xmake-update-v2.8.3.md --- ## Introduction of new features In the new version, we have added breakpoint debugging support for Xmake's own source code, which can help contributors to get familiar with xmake's source code more quickly, and also help users to debug and analyse their own project's configure scripts. In addition, the number of packages in our [xmake-repo](https://github.com/xmake-io/xmake-repo) repository is about to exceed 1100, with more than 100 packages added in just one month, thanks to @star-hengxing's contribution. At the same time, we focused on improving build support for Wasm and Qt6 for wasm. ### Breakpoint Debugging Xmake In version 2.8.3, we added Lua breakpoint debugging support, with [VSCode-EmmyLua](https://github.com/EmmyLua/VSCode-EmmyLua) plugin, we can easily debug Xmake source code in VSCode breakpoints. First of all, we need to install VSCode-EmmyLua plugin in VSCode's plugin market, and then run the following command to update the xmake-repo repository to keep it up-to-date. ```bash $ xrepo update-repo ``` :::NOTE Xmake also needs to be kept up to date. ::: Then, execute the following command in your own project directory: ```bash $ xrepo env -b emmylua_debugger -- xmake build ``` The `xrepo env -b emmylua_debugger` is used to bind the EmmyLua debugger plugin environment, and the arguments after `--` are the actual xmake commands we need to debug. Usually we just debug the `xmake build` build, but if you want to debug other commands, you can tweak it yourself, for example, if you want to debug the `xmake install -o /tmp` install command, you can change it to: ```bash $ xrepo env -b emmylua_debugger -- xmake install -o /tmp ``` After executing the above command, it will not exit immediately, it will remain in a waiting debugging state, possibly without any output. At this point, instead of exiting it, let's go ahead and open VSCode and open Xmake's Lua script source directory in VSCode. That is, this directory: [Xmake Lua Scripts](https://github.com/xmake-io/xmake/tree/master/xmake), which we can download locally or directly open the lua script directory in the Xmake installation directory. Then switch to VSCode's debugging tab and click `RunDebug` -> `Emmylua New Debug` to connect to our `xmake build` command debugger and start debugging. As you can see below, the default start breakpoint will automatically break inside `debugger:_start_emmylua_debugger`, and we can click on the single-step to jump out of the current function, which will take us to the main entry. ![](/assets/img/manual/xmake-debug.png) Then set your own breakpoint and click Continue to Run to break to the code location you want to debug. We can also set breakpoints in our project's configuration scripts, which also allows us to quickly debug our own configuration scripts, not just Xmake's own source code. ![](/assets/img/manual/xmake-debug2.png) ### Remote debugging xmake Version 2.8.3 now supports remote debugging, but this feature is mainly for the author: because the author's development computer is a mac, but sometimes he still needs to be able to debug xmake source scripts on windows. But debugging in a virtual machine is too laggy, not good experience, and the author's own computer does not have enough disk space, so I usually connect to a separate windows host to debug xmake source code remotely. Let's start the remote compilation service on the windows machine: ```bash $ xmake service ``` Then locally, open the project directory where you want to build, make a remote connection, and then run `xmake service --sync --xmakesrc=` to synchronise the local source: ```bash $ xmake service --connect $ xmake service --sync --xmakesrc=~/projects/personal/xmake/xmake/ $ xmake build $ xmake run ``` This way, we can modify the xmake script source locally, sync it to a remote windows machine, and then execute the xmake build command remotely to get the corresponding debug output and analyse the build behaviour. We can also pull the remote files back to the local machine for analysis with the `xmake service --pull=` command. Note: See [Remote Build Documentation](https://xmake.io/#/features/remote_build) for a detailed description of remote build features. ![](/assets/img/manual/xmake-remote.png) ### Support for Cppfront programmes We have also added a new build rule to support the compilation of [cppfront](https://github.com/hsutter/cppfront) programs: ```bash add_rules("mode.debug", "mode.release") add_requires("cppfront") target("test") add_rules("cppfront") set_kind("binary") add_files("src/*.cpp2") add_packages("cppfront") ``` ### Added utils.hlsl2spv build rule We've already provided the `utils.glsl2spv` rule to support the compilation and use of glsl, but now we've added the `utils.hlsl2spv` rule to support the compilation of hlsl. ```bash add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.hlsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.hlsl", "src/*.hlsl") add_packages("directxshadercompiler") ``` For a detailed description of this rule, see the documentation: [utils.glsl2spv](https://xmake.io/#/manual/custom_rule#utilsglsl2spv), both are used in a similar way and principle. ### Add lib.lua.package module. Xmake restricts access to native lua modules and interfaces by default, and this module is mainly used to open up some of the APIs provided by lua to be used on demand. Currently, it only provides the `package.loadlib` interface for loading lua modules from the native so/dylib/dll dynamic library. This is typically used in high-performance scenarios. ```Lua import("lib.lub.package") local script = package.loadlib("/xxx/libfoo.so", "luaopen_mymodule") local mymodule = script() mymodule.hello() ``` ### Improved Address sanitizer support Address Sanitizer (ASan) is a fast memory error detection tool that is built-in by the compiler, and normally we need to configure `-fsanitize-address` in both compilation and linking flags to enable it properly. In previous versions, we supported this by configuring `add_rules("mode.asan")` and then `xmake f -m asan` to switch build modes. But there are a few problems with this: 1. it doesn't work on dependent packages 2. you need to switch build modes 3. asan and ubsan cannot be detected at the same time. Therefore, in the new version, we have switched to using policy to support them better. We can quickly enable it globally by turning on the `build.sanitizer.address` policy, which will enable compiled applications to support ASan detection directly. For example, we can enable it from the command line: ```bash $ xmake f --policies=build.sanitizer.address ``` It can also be enabled globally via the interface configuration: ```lua set_policy("build.sanitizer.address", true) ``` Of course, we can also enable it for a specific target individually, and if we configure it globally, we can enable it for all dependent packages at the same time. ```lua set_policy("build.sanitizer.address", true) add_requires("zlib") add_requires("libpng") ``` It is equivalent to setting the asan configuration for each package in turn. ```lua add_requires("zlib", {configs = {asan = true}}) add_requires("libpng", {configs = {asan = true}}) ``` Alternatively, we can have multiple sanitizer detections in effect at the same time, for example: ```lua set_policy("build.sanitizer.address", true) set_policy("build.sanitizer.undefined", true) ``` or ```bash $ xmake f --policies=build.sanitizer.address,build.sanitizer.undefined ``` In addition to Asan, we provide other similar policies for detecting threads, memory leaks, and so on. * build.sanitizer.thread * build.sanitizer.memory * build.sanitizer.leak * build.sanitizer.undefined ### Autobuild before running We have added a new `run.atuobuild` policy to adjust the behaviour of `xmake run`. By default, running `xmake run` does not automatically build the target application, but prompts the user to build it manually if the application has not yet been compiled. By turning on this policy, we can automatically build the target program before running it. ```bash $ xmake f --policies=run.autobuild $ xmake run ``` If you want this policy to take effect globally, you can turn it on globally. ```bash $ xmake g --policies=run.autobuild ``` ### Shallow build of a specified target When we specify to rebuild a target, if it has a lot of dependent targets, then they are usually all rebuilt. ```bash $ xmake --rebuild foo rebuild foo rebuild foo.dep1 rebuild foo.dep2 ... ``` This can be very slow for large projects with a lot of target dependencies, and can mean that half of the project has to be rebuilt. In the new version, we've added a `--shallow` parameter to tell Xmake to only rebuild the specified target, and not to rebuild all of its dependencies. ```bash $ xmake --rebuild --shallow foo only rebuild foo ``` ### Improved warning settings The `set_warnings` interface has new `extra` and `pedantic` settings and supports combining multiple warning values. ```lua set_warnings("all", "extra") set_warnings("pedantic") ``` ### Improving Wasm Builds Now we can pull the emscripten toolchain ourselves and use it to build wasm programs automatically with the following configuration. ```lua if is_plat("wasm") then add_requires("emscripten") set_toolchains("emcc@emscripten") end ``` Simply just run ```bash $ xmake f -p wasm $ xmake ``` to build the wasm application, so you don't have to manually install emscripten yourself, which is much more convenient. We also have good support for Qt6 for wasm. ## Changelog ### New features * [#4122](https://github.com/xmake-io/xmake/issues/4122): Support Lua Debugger (EmmyLua) * [#4132](https://github.com/xmake-io/xmake/pull/4132): Support cppfront * [#4147](https://github.com/xmake-io/xmake/issues/4147): Add hlsl2spv rule * [#4226](https://github.com/xmake-io/xmake/issues/4226): Support sanitizers for package and policy * Add lib.lua.package module * Add `run.autobuild` policy * Add global policies `xmake g --policies=` ### Changes * [#4119](https://github.com/xmake-io/xmake/issues/4119): Improve to support emcc toolchain and emscripten package * [#4154](https://github.com/xmake-io/xmake/issues/4154): Add `xmake -r --shallow target` to rebuild target without deps * Add global ccache storage directory * [#4137](https://github.com/xmake-io/xmake/issues/4137): Support Qt6 for Wasm * [#4173](https://github.com/xmake-io/xmake/issues/4173): Add recheck argument to on\_config * [#4200](https://github.com/xmake-io/xmake/pull/4200): Improve remote build to support debugging xmake source code. * [#4209](https://github.com/xmake-io/xmake/issues/4209): Add extra and pedantic warnings ### Bugs fixed * [#4110](https://github.com/xmake-io/xmake/issues/4110): Fix extrafiles * [#4115](https://github.com/xmake-io/xmake/issues/4115): Fix compile\_commands generator for clangd * [#4199](https://github.com/xmake-io/xmake/pull/4199): Fix compile\_commands generator for c++ modules * Fix os.mv fail on window * [#4214](https://github.com/xmake-io/xmake/issues/4214): Fix rust workspace build error --- --- url: /zh/posts/xmake-update-v2.8.3.md --- ## 新特性介绍 新版本中,我们新增了 Xmake 自身源码的断点调试支持,这可以帮助贡献者更加快速的熟悉 xmake 源码,也可以帮助用户去快速调试分析自身项目的配置脚本。 另外,我们 [xmake-repo](https://github.com/xmake-io/xmake-repo) 官方仓库包的数量也即将突破 1100,短短一个月的时间,就新增了 100 多个包,非常感谢 @star-hengxing 的贡献。 同时,我们重点改进了 Wasm 的构建支持,以及 Qt6 for wasm 的支持。 ### 断点调试 Xmake 源码 2.8.3 版本,我们新增了 Lua 断点调试支持,配合 [VSCode-EmmyLua](https://github.com/EmmyLua/VSCode-EmmyLua) 插件,我们可以很方便的在 VSCode 中断点调试 Xmake 自身源码。 首先,我们需要在 VSCode 的插件市场安装 VSCode-EmmyLua 插件,然后执行下面的命令更新下 xmake-repo 仓库保持最新。 ```bash xrepo update-repo ``` :::注意 Xmake 也需要保持最新版本。 ::: 然后,在自己的工程目录下执行以下命令: ```bash $ xrepo env -b emmylua_debugger -- xmake build ``` 其中 `xrepo env -b emmylua_debugger` 用于绑定 EmmyLua 调试器插件环境,而 `--` 后面的参数,就是我们实际需要被调试的 xmake 命令。 通常我们仅仅调试 `xmake build` 构建,如果想要调试其他命令,可以自己调整,比如想要调试 `xmake install -o /tmp` 安装命令,那么可以改成: ```bash $ xrepo env -b emmylua_debugger -- xmake install -o /tmp ``` 执行完上面的命令后,它不会立即退出,会一直处于等待调试状态,有可能没有任何输出。 这个时候,我们不要急着退出它,继续打开 VSCode,并在 VSCode 中打开 Xmake 的 Lua 脚本源码目录。 也就是这个目录:[Xmake Lua Scripts](https://github.com/xmake-io/xmake/tree/master/xmake),我们可以下载的本地,也可以直接打开 Xmake 安装目录中的 lua 脚本目录。 然后切换到 VSCode 的调试 Tab 页,点击 `RunDebug` -> `Emmylua New Debug` 就能连接到我们的 `xmake build` 命令调试端,开启调试。 如下图所示,默认的起始断点会自动中断到 `debugger:_start_emmylua_debugger` 内部,我们可以点击单步跳出当前函数,就能进入 main 入口。 ![](/assets/img/manual/xmake-debug.png) 然后设置自己的断点,点击继续运行,就能中断到自己想要调试的代码位置。 我们也可以在项目工程的配置脚本中设置断点,也可以实现快速调试自己的配置脚本,而不仅仅是 Xmake 自身源码。 ![](/assets/img/manual/xmake-debug2.png) ### 远程调试 Xmake 源码 2.8.3 版本现在也能支持远程调试,其实这个功能主要是给作者用的,因为作者本人的开发电脑是 mac,但是有时候还是需要能够在 windows 上调试 xmake 源码脚本。 但是在虚拟机中调试,太卡,体验不好,并且作者本人的电脑磁盘空间不够,因此我通常会远程连到单独的 windows 主机上去调试 xmake 源码。 我们先在 windows 机器上开启远程编译服务: ```bash $ xmake service ``` 然后本机打开需要构建的工程目录,执行远程连接,然后执行 `xmake service --sync --xmakesrc=` 去同步本地源码: ```bash $ xmake service --connect $ xmake service --sync --xmakesrc=~/projects/personal/xmake/xmake/ $ xmake build $ xmake run ``` 这样,我们就能本地修改 xmake 脚本源码,然后同步到远程 windows 机器上,然后远程执行 xmake 构建命令,获取对应的调试输出,以及分析构建行为。 我们也能够通过 `xmake service --pull=` 命令,回拉远程的文件到本地,进行分析。 注:详细的远程编译特性说明,见 [远程编译文档](https://xmake.io/#/zh-cn/features/remote_build)。 ![](/assets/img/manual/xmake-remote.png) ### 支持 Cppfront 程序 我么也新增了一个构建规则,用于支持 [cppfront](https://github.com/hsutter/cppfront) 程序的编译: ```bash add_rules("mode.debug", "mode.release") add_requires("cppfront") target("test") add_rules("cppfront") set_kind("binary") add_files("src/*.cpp2") add_packages("cppfront") ``` ### 新增 utils.hlsl2spv 构建规则 早期我们已经提供了 `utils.glsl2spv` 规则去支持 glsl 的编译和使用,现在我们又新增了 `utils.hlsl2spv` 规则,实现对 hlsl 的编译支持。 ```bash add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.hlsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.hlsl", "src/*.hlsl") add_packages("directxshadercompiler") ``` 关于这个规则的详细描述,可以参考文档:[utils.glsl2spv](https://xmake.io/zh/),两者的使用方式和原理都是类似的。 ### 新增 lib.lua.package 模块 Xmake 默认会限制对 lua 原生模块和接口的访问,而这个模块主要用于开放一些 lua 原生提供的 API,用户可以按需使用。 目前,它仅仅提供了 `package.loadlib` 接口,用于加载本地 so/dylib/dll 动态库中的 lua 模块。 这通常用于一些高性能要求的场景。 ```lua import("lib.lub.package") local script = package.loadlib("/xxx/libfoo.so", "luaopen_mymodule") local mymodule = script() mymodule.hello() ``` ### 改进 Address sanitizer 支持 Address Sanitizer(ASan)是一个快速的内存错误检测工具,由编译器内置支持,通常我们需要在编译和链接的 flags 中同时配置 `-fsanitize-address` 才能正确开启。 而之前的版本中,我们是通过配置 `add_rules("mode.asan")` 然后 `xmake f -m asan` 去切换构建模式的方式来支持。 但这会有一些问题: 1. 不能对依赖包生效 2. 需要切换构建模式 3. 不能同时检测 asan 和 ubsan 因此,新版本中,我们改用 policy 去更好的支持它们。 而我们可以通过开启 `build.sanitizer.address` 策略,就可以快速全局启用它,这会使得编译出来的程序,直接支持 ASan 检测。 例如,我们可以通过命令行的方式去启用: ```bash $ xmake f --policies=build.sanitizer.address ``` 也可以通过接口配置去全局启用: ```lua set_policy("build.sanitizer.address", true) ``` 当然,我们也可以单独对某个特定的 target 去配置开启,另外,如果全局配置它,我们就可以同时对所有依赖包也生效。 ```lua set_policy("build.sanitizer.address", true) add_requires("zlib") add_requires("libpng") ``` 它等价于,对每个包依次设置 asan 配置。 ```lua add_requires("zlib", {configs = {asan = true}}) add_requires("libpng", {configs = {asan = true}}) ``` 另外,我们也可以同时生效多个 sanitizer 检测,例如: ```lua set_policy("build.sanitizer.address", true) set_policy("build.sanitizer.undefined", true) ``` 或者 ```bash $ xmake f --policies=build.sanitizer.address,build.sanitizer.undefined ``` 除了 Asan,我们还提供了其他类似的 policies,用于检测线程,内存泄漏等问题。 * build.sanitizer.thread * build.sanitizer.memory * build.sanitizer.leak * build.sanitizer.undefined ### 运行前自动构建 我们新增了 `run.atuobuild` 策略用于调整 `xmake run` 的行为,默认情况下,执行 `xmake run` 并不会自动构建目标程序,如果程序还没被编译,就是提示用户手动构建一下。 而开启这个策略,我们就可以在运行程序前,先自动构建对应的目标程序。 ```bash $ xmake f --policies=run.autobuild $ xmake run ``` 如果想要全局生效这个策略,可以全局开启它。 ```bash $ xmake g --policies=run.autobuild ``` ### 浅构建指定目标 当我们指定重新构建某个目标的时候,如果它有很多的依赖目标,那么通常都会全部被重新构建。 ```bash $ xmake --rebuild foo rebuild foo rebuild foo.dep1 rebuild foo.dep2 ... ``` 这对于一些大型项目,依赖了大量 target 时候,非常影响编译速度,几乎等于大半个项目都要被重新构建。 新版本中,我们新增了 `--shallow` 参数,用于告诉 Xmake,当前仅仅重新构建指定的 target,它的所有依赖不需要被全部重新编译。 ```bash $ xmake --rebuild --shallow foo only rebuild foo ``` ### 改进警告设置 `set_warnings` 接口新增 `extra` 和 `pedantic` 设置,并且支持多个警告值组合。 ```lua set_warnings("all", "extra") set_warnings("pedantic") ``` ### 改进 Wasm 构建 现在,我们可以通过下面的配置,自己拉取 emscripten 工具链,并自动使用它去构建 wasm 程序。 ```lua if is_plat("wasm") then add_requires("emscripten") set_toolchains("emcc@emscripten") end ``` 仅仅只需要执行 ```bash $ xmake f -p wasm $ xmake ``` 就可以完成 wasm 程序构建,用户可以不用自己手动安装 emscripten,更加的方便。 另外,我们也对 Qt6 for wasm 做了很好的支持。 ## 更新日志 ### 新特性 * [#4122](https://github.com/xmake-io/xmake/issues/4122): 支持 Lua 调试 (EmmyLua) * [#4132](https://github.com/xmake-io/xmake/pull/4132): 支持 cppfront * [#4147](https://github.com/xmake-io/xmake/issues/4147): 添加 hlsl2spv 规则 * 添加 lib.lua.package 模块 * [#4226](https://github.com/xmake-io/xmake/issues/4226): 新增 asan 相关策略和对包的支持 * 添加 `run.autobuild` 策略开启运行前自动构建 * 添加全局策略 `xmake g --policies=` ### 改进 * [#4119](https://github.com/xmake-io/xmake/issues/4119): 改进支持 emcc 工具链和 emscripten 包的整合 * [#4154](https://github.com/xmake-io/xmake/issues/4154): 添加 `xmake -r --shallow target` 去改进重建目标,避免重建所有依赖目标 * 添加全局 ccache 存储目录 * [#4137](https://github.com/xmake-io/xmake/issues/4137): 改进 Qt,支持 Qt6 for Wasm * [#4173](https://github.com/xmake-io/xmake/issues/4173): 添加 recheck 参数到 on\_config * [#4200](https://github.com/xmake-io/xmake/pull/4200): 改进远程构建,支持调试本地 xmake 源码 * [#4209](https://github.com/xmake-io/xmake/issues/4209): 添加 extra 和 pedantic 警告 ### Bugs 修复 * [#4110](https://github.com/xmake-io/xmake/issues/4110): 修复 extrafiles * [#4115](https://github.com/xmake-io/xmake/issues/4115): 修复 compile\_commands 生成器 * [#4199](https://github.com/xmake-io/xmake/pull/4199): 修复 compile\_commands 生成器对 c++ modules 的支持 * 修复 os.mv 在 windows 上跨驱动盘失败问题 * [#4214](https://github.com/xmake-io/xmake/issues/4214): 修复 rust workspace 构建问题 --- --- url: /posts/xmake-update-v2.8.5.md --- ## Introduction of new features Before introducing new features, we have good news to tell you that Xmake has recently entered Debian's official repository: \[https://packages.debian.org/sid/xmake]\(https://packages.debian.org/ sid/xmake), When Ubuntu 24.04 is released in April next year, we should be able to quickly install Xmake directly through the `apt install xmake` command. I would also like to thank @Lance Lin for his help. He helped us maintain and upload the Xmake package to the Debian repository throughout the whole process. Thank you very much! Next, let’s introduce some changes introduced in version 2.8.5. This version brings many new features, especially support for link sorting, link groups, and support for `xmake test` built-in unit tests. In addition, we have also added build support for the Apple XROS platform, which can be used to build programs on Apple's new VisionOS. We also provide a more flexible and versatile `check_sizeof` detection interface for quickly detecting the size of types. ### Link reordering support This is a requirement that has existed for more than two years and is mainly used to adjust the link order within the target. Since xmake provides `add_links`, `add_deps`, `add_packages`, `add_options` interfaces, you can configure links in targets, dependencies, packages and options, although the link order of `add_links` itself can be adjusted according to the order of addition. However, the link order between links, deps and packages can only be generated in a fixed order and cannot be adjusted flexibly. This is a bit inadequate for some complex projects. In this version, we have completely solved this problem and added the `add_linkorders` interface, which can be used to configure various link orders introduced by targets, dependencies, packages, options, and link groups. For more details and background, see: [#1452](https://github.com/xmake-io/xmake/issues/1452) #### Sort links In order to more flexibly adjust the various link orders within the target, we can implement it through the new interface `add_linkorders`, for example: ```lua add_links("a", "b", "c", "d", "e") -- e -> b -> a add_linkorders("e", "b", "a") --e->d add_linkorders("e", "d") ``` add\_links is the configured initial link order, and then we configure two local link dependencies `e -> b -> a` and `e -> d` through add\_linkorders. xmake will internally generate a DAG graph based on these configurations, and use topological sorting to generate the final link sequence and provide it to the linker. Of course, if there is a circular dependency and a cycle is created, it will also provide warning information. #### Sorting links and link groups In addition, we can also solve the problem of circular dependencies by configuring link groups through `add_linkgroups`. And `add_linkorders` can also sort link groups. ```lua add_links("a", "b", "c", "d", "e") add_linkgroups("c", "d", {name = "foo", group = true}) add_linkorders("e", "linkgroup::foo") ``` If we want to sort link groups, we need to give each link group a name, `{name = "foo"}`, and then we can reference the configuration through `linkgroup::foo` in `add_linkorders`. #### Sort links and frameworks We can also sort links and frameworks for macOS/iPhoneOS. ```lua add_links("a", "b", "c", "d", "e") add_frameworks("Foundation", "CoreFoundation") add_linkorders("e", "framework::CoreFoundation") ``` #### Complete example For a complete example, we can look at: ```lua add_rules("mode.debug", "mode.release") add_requires("libpng") target("bar") set_kind("shared") add_files("src/foo.cpp") add_linkgroups("m", "pthread", {whole = true}) target("foo") set_kind("static") add_files("src/foo.cpp") add_packages("libpng", {public = true}) target("demo") set_kind("binary") add_deps("foo") add_files("src/main.cpp") if is_plat("linux", "macosx") then add_syslinks("pthread", "m", "dl") end if is_plat("macosx") then add_frameworks("Foundation", "CoreFoundation") end add_linkorders("framework::Foundation", "png16", "foo") add_linkorders("dl", "linkgroup::syslib") add_linkgroups("m", "pthread", {name = "syslib", group = true}) ``` The complete project is at: [linkorders example](https://github.com/xmake-io/xmake/blob/master/tests/projects/c%2B%2B/linkorders/xmake.lua) ### Link group support In addition, in this version, we have also added native support for the link group, which is currently mainly used for compilation on the Linux platform and only supports the gcc/clang compiler. It should be noted that the concept of link group in gcc/clang mainly refers to: `-Wl,--start-group` Xmake is aligned and encapsulated, further abstracted, and not only used to process `-Wl,--start-group`, but also `-Wl,--whole-archive` and `-Wl,-Bstatic` . Below we will explain them one by one. For more details, see: [#1452](https://github.com/xmake-io/xmake/issues/1452) #### --start-group support `-Wl,--start-group` and `-Wl,--end-group` are linker options for handling complex library dependencies, ensuring that the linker can resolve symbolic dependencies and successfully connect multiple libraries. In xmake, we can achieve this in the following way: ```lua add_linkgroups("a", "b", {group = true}) ``` It will generate the corresponding `-Wl,--start-group -la -lb -Wl,--end-group` link options. If there is a symbolic circular dependency between libraries a and b, no link error will be reported and the link can be successful. For unsupported platforms and compilations, it will fall back to `-la -lb` #### --whole-archive support `--whole-archive` is a linker option commonly used when dealing with static libraries. Its function is to tell the linker to include all object files in the specified static library into the final executable file, not just the object files that satisfy the current symbol dependencies. This can be used to ensure that all code for certain libraries is linked, even if they are not directly referenced in the current symbol dependencies. For more information, please refer to the gcc/clang documentation. In xmake, we can achieve this in the following way: ```lua add_linkgroups("a", "b", {whole = true}) ``` It will generate the corresponding `-Wl,--whole-archive -la -lb -Wl,--no-whole-archive` link options. For unsupported platforms and compilations, it will fall back to `-la -lb` Additionally, we can configure group/whole at the same time: ```lua add_linkgroups("a", "b", {whole = true, group = true}) ``` #### -Bstatic support `-Bstatic` is also an option for compilers (such as gcc) to instruct the compiler to use only static libraries and not shared libraries when linking. For more information, please refer to the gcc/clang documentation. In xmake, we can achieve this in the following way: ```lua add_linkgroups("a", "b", {static = true}) ``` It will generate the corresponding `-Wl,-Bstatic -la -lb -Wl,-Bdynamic` linkage options. ### Unit testing support In the new version, we have also added a built-in test command: `xmake test`. We only need to configure some test cases through add\_tests on the target that needs to be tested to automatically execute the test. Even if the current target is set to `set_default(false)`, when executing tests, xmake will still automatically compile them first, and then automatically run all tests. We can first look at an overall example to get a rough idea of what it looks like. ```lua add_rules("mode.debug", "mode.release") for _, file in ipairs(os.files("src/test_*.cpp")) do local name = path.basename(file) target(name) set_kind("binary") set_default(false) add_files("src/" .. name .. ".cpp") add_tests("default") add_tests("args", {runargs = {"foo", "bar"}}) add_tests("pass_output", {trim_output = true, runargs = "foo", pass_outputs = "hello foo"}) add_tests("fail_output", {fail_outputs = {"hello2 .*", "hello xmake"}}) end ``` This example automatically scans the `test_*.cpp` source files in the source code directory, and then automatically creates a test target for each file. It is set to `set_default(false)`, which means that under normal circumstances, it will not be compiled by default. they. However, if you execute `xmake test` for testing, they will be automatically compiled and then tested. The running effect is as follows: ```bash ruki-2:test ruki$ xmake test running tests... [2%]: test_1/args .................................. passed 7.000s [5%]: test_1/default .................................... passed 5.000s [ 8%]: test_1/fail_output .................................... passed 5.000s [ 11%]: test_1/pass_output .................................... passed 6.000s [ 13%]: test_2/args .................................... passed 7.000s [ 16%]: test_2/default .................................... passed 6.000s [ 19%]: test_2/fail_output .................................... passed 6.000s [ 22%]: test_2/pass_output .................................... passed 6.000s [ 25%]: test_3/args .................................... passed 7.000s [ 27%]: test_3/default .................................... passed 7.000s [ 30%]: test_3/fail_output .................................... passed 6.000s [ 33%]: test_3/pass_output .................................... passed 6.000s [ 36%]: test_4/args .................................... passed 6.000s [ 38%]: test_4/default .................................... passed 6.000s [ 41%]: test_4/fail_output .................................... passed 5.000s [ 44%]: test_4/pass_output .................................... passed 6.000s [ 47%]: test_5/args .................................... passed 5.000s [ 50%]: test_5/default .................................... passed 6.000s [ 52%]: test_5/fail_output .................................... failed 6.000s [ 55%]: test_5/pass_output .................................... failed 5.000s [ 58%]: test_6/args .................................... passed 7.000s [ 61%]: test_6/default .................................... passed 6.000s [ 63%]: test_6/fail_output .................................... passed 6.000s [ 66%]: test_6/pass_output .................................... passed 6.000s [ 69%]: test_7/args .................................... failed 6.000s [ 72%]: test_7/default .................................... failed 7.000s [ 75%]: test_7/fail_output .................................... failed 6.000s [ 77%]: test_7/pass_output .................................... failed 5.000s [ 80%]: test_8/args .................................... passed 7.000s [ 83%]: test_8/default .................................... passed 6.000s [ 86%]: test_8/fail_output .................................... passed 6.000s [ 88%]: test_8/pass_output .................................... failed 5.000s [ 91%]: test_9/args .................................... passed 6.000s [ 94%]: test_9/default .................................... passed 6.000s [ 97%]: test_9/fail_output .................................... passed 6.000s [100%]: test_9/pass_output .................................... passed 6.000s 80% tests passed, 7 tests failed out of 36, spent 0.242s ``` ![](/assets/img/manual/xmake-test1.png) We can also execute `xmake test -vD` to view detailed error information about test failures: ![](/assets/img/manual/xmake-test2.png) #### Running Specified Test Targets We can also specify to run a specific test of a target: ```bash $ xmake test targetname/testname ``` Or run all tests of a target, or a batch of tests, by pattern matching: ```bash $ xmake test targetname/* $ xmake test targetname/foo* ``` We can also run tests with the same name across all targets: ```bash $ xmake test */testname ``` #### Parallel Test Execution Actually, it runs in parallel by default, but we can adjust the parallelism level through `-jN`. ```bash $ xmake test -jN ``` #### Group Test Execution ```bash $ xmake test -g "foo" $ xmake test -g "foo*" ``` #### Adding Tests to Target (No Parameters) If no parameters are configured, and only the test name is configured to `add_tests`, it will only test whether the target program will run and fail, judging whether the test passes based on the exit code. ``` target("test") add_tests("testname") ``` #### Configuring Run Arguments We can also configure the arguments that the test needs to run through `{runargs = {"arg1", "arg2"}}` in `add_tests`. In addition, a target can configure multiple test cases at the same time, and each test case can run independently without conflicts. ```lua target("test") add_tests("testname", {runargs = "arg1"}) add_tests("testname", {runargs = {"arg1", "arg2"}}) ``` If we haven't configured runargs to `add_tests`, we will also try to get the run arguments set by `set_runargs` from the bound target. ```lua target("test") add_tests("testname") set_runargs("arg1", "arg2") ``` #### Configuring Run Directory We can also set the current working directory for test execution through rundir, for example: ```lua target("test") add_tests("testname", {rundir = os.projectdir()}) ``` If we haven't configured rundir to `add_tests`, we will also try to get the run directory set by `set_rundir` from the bound target. ```lua target("test") add_tests("testname") set_rundir("$(projectdir)") ``` #### Configuring Run Environment We can also set some environment variables at runtime through runenvs, for example: ```lua target("test") add_tests("testname", {runenvs = {LD_LIBRARY_PATH = "/lib"}}) ``` If we haven't configured runenvs to `add_tests`, we will also try to get the run environment set by `add_runenvs` from the bound target. ```lua target("test") add_tests("testname") add_runenvs("LD_LIBRARY_PATH", "/lib") ``` #### Matching Output Results By default, `xmake test` will judge whether the test passes based on whether the exit code of the test run is 0. Of course, we can also judge whether the test passes by configuring whether the output results of the test run meet our specified matching patterns. Mainly controlled by these two parameters: | Parameter | Description | | --- | --- | | pass\_outputs | If output matches, test passes | | fail\_outputs | If output matches, test fails | The `pass_outputs` and `fail_outputs` passed in are a list of lua matching patterns, but the patterns have been slightly simplified, such as the handling of `*`. If the match is successful, the test passes. You can configure it like this: ```lua target("test") add_tests("testname1", {pass_outputs = "hello"}) add_tests("testname2", {pass_outputs = "hello *"}) add_tests("testname3", {pass_outputs = {"hello", "hello *"}}) ``` If the match is successful, the test fails. You can configure it like this: ```lua target("test") add_tests("testname1", {fail_outputs = "hello"}) add_tests("testname2", {fail_outputs = "hello *"}) add_tests("testname3", {fail_outputs = {"hello", "hello *"}}) ``` We can also configure them simultaneously: ```lua target("test") add_tests("testname", {pass_outputs = "foo", fail_outputs = "hello"}) ``` Since some test output results will have some newline or other blank characters at the end, which interferes with the matching mode, we can configure `trim_output = true` to truncate the blank characters before matching. ```lua target("test") add_tests("testname", {trim_output = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` We can also configure `{plain = true}` to disable lua pattern matching and only do the most basic flat text matching. ```lua target("test") add_tests("testname", {plain = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` #### Configure test group We can also configure a test group through `group = "foo"` for group testing: ```lua target("test") add_tests("testname1", {group = "foo"}) add_tests("testname2", {group = "foo"}) add_tests("testname3", {group = "bar"}) add_tests("testname4", {group = "bae"}) ``` Where testname1/testname2 is a group foo, and the other two are in another group. Then, we can use `xmake test -g groupname` to perform group testing. ```bash $ xmake test -g "foo" $ xmake test -g "foo*" ``` :::NOTE Running grouping also supports pattern matching. ::: In addition, if the `group` parameter is not set to `add_tests`, we can also get the group name bound to the target by default. ```lua target("test") add_tests("testname") set_group("foo") ``` #### Custom test script We have also added `before_test`, `on_test` and `after_test` configuration scripts. Users can customize them in the rule and target fields to implement customized test execution. ```lua target("test") on_test(function (target, opt) print(opt.name, opt.runenvs, opt.runargs, opt.pass_outputs) -- do test --... -- passed return true -- failed return false, errors end) ``` Among them, all parameters passed into `add_tests` can be obtained in opt. We customize the test logic in on\_test, and then return true to indicate that the test passed, return false to indicate that the test failed, and then continue to return the error message of test failure. #### Automated build Since the test target usually does not need to be built during the normal development build phase, we will set `set_default(false)`. ```lua target("test") add_tests("testname") set_default(false) ``` However, when running `xmake test` for testing, the targets corresponding to these tests will still be automatically built to ensure that they can be run. ```bash $ xmake test [25%]: cache compiling.release src/main.cpp [50%]: linking.release test running tests... [100%]: test/testname ............................. passed 6.000s 100% tests passed, 0 tests failed out of 1, spent 0.006s ``` #### Terminate if the first test fails By default, `xmake test` will wait until all tests have been run, no matter how many of them failed. Sometimes, we want to interrupt the test directly if the first test fails, then we can enable it through the following configuration: ```lua set_policy("test.return_zero_on_failure", true) ``` #### If the test fails, return 0 By default, as long as a test fails, it will return a non-zero exit code when `xmake test` is completed. This is very useful for some CI environments and can interrupt other CI scripts to continue running. Then the trigger signal tells CI that we need to generate test reports and alarms. Then, if we want to suppress this behavior, we can force the exit code of `xmake test` to always be set to 0. ```lua set_policy("test.return_zero_on_failure", true) ``` #### Just test compilation Sometimes, we just want to test whether the code compiles or fails without running them. This can be achieved by configuring `build_should_pass` and `build_should_fail`. ```lua target("test_10") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_fail", {build_should_fail = true}) target("test_11") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_pass", {build_should_pass = true}) ``` This is usually used in scenarios with `static_assert` in some test code, for example: ```c++ template bool foo(T val) { if constexpr (std::is_same_v) { printf("int!\n"); } else if constexpr (std::is_same_v) { printf("float!\n"); } else { static_assert(false, "unsupported type"); } } int main(int, char**) { foo("BAD"); return 0; } ``` #### Configure additional code compilation When configuring test cases, we can also configure additional code that needs to be compiled for each test, as well as some macro definitions to implement inline testing. xmake will compile an independent executable program for each test to run it, but this will not affect the compilation results of the target in the production environment. ```lua target("test_13") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_1", {files = "tests/stub_1.cpp", defines = "STUB_1"}) target("test_14") set_kind("binary") set_default(false) add_files("src/test_2.cpp") add_tests("stub_2", {files = "tests/stub_2.cpp", defines = "STUB_2"}) target("test_15") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_n", {files = "tests/stub_n*.cpp", defines = "STUB_N"}) ``` Taking doctest as an example, we can externally unit test without modifying any main.cpp: ```lua add_rules("mode.debug", "mode.release") add_requires("doctest") target("doctest") set_kind("binary") add_files("src/*.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { files = testfile, remove_files = "src/main.cpp", languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` Defining DOCTEST\_CONFIG\_IMPLEMENT\_WITH\_MAIN will introduce additional main entry function, so we need to configure remove\_files to remove the existing main.cpp file. The running effect is as follows: ```bash ruki-2:doctest ruki$ xmake test running tests... [50%]: doctest/test_1 ........................ failed 0.009s [100%]: doctest/test_2 ........................ passed 0.009s 50% tests passed, 1 tests failed out of 2, spent 0.019s ruki-2:doctest ruki$ xmake test -v running tests... [50%]: doctest/test_1....................... failed 0.026s [doctest] doctest version is "2.4.11" [doctest] run with "--help" for options ================================================== ============================= tests/test_1.cpp:7: TEST CASE: testing the factorial function tests/test_1.cpp:8: ERROR: CHECK( factorial(1) == 10 ) is NOT correct! values: CHECK( 1 == 10 ) ================================================== ============================= [doctest] test cases: 1 | 0 passed | 1 failed | 0 skipped [doctest] assertions: 4 | 3 passed | 1 failed | [doctest] Status: FAILURE! run failed, exit code: 1 [100%]: doctest/test_2 ........................ passed 0.010s 50% tests passed, 1 tests failed out of 2, spent 0.038s ``` #### Test dynamic library Usually, `add_tests` is only used to run tests on executable programs. Running dynamic libraries requires an additional main entry, so we need to configure an additional executable program to load it, for example: ```lua target("doctest_shared") set_kind("shared") add_files("src/foo.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { kind = "binary", files = testfile, languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` Each unit test can be changed to a binary executable program through `kind = "binary"`, and the main entry function can be introduced through `DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN`. This enables external runnable unit tests in dynamic library targets. ### Added type size detection In previous versions, we could implement type detection through `check_csnippets` and `output = true`. ```lua check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` But this way is to extract the type size information by trying to run the test code, and then getting the running output results. This does not apply to cross-compilation. In version 2.8.5, we added the `check_sizeof` auxiliary interface, which can extract type size information by directly parsing the binary file of the test program. Since there is no need to run tests, this method not only supports cross-compilation, but also greatly improves detection efficiency and is simpler to use. ```lua includes("@builtin/check") target("test") set_kind("static") add_files("*.cpp") check_sizeof("LONG_SIZE", "long") check_sizeof("STRING_SIZE", "std::string", {includes = "string"}) ``` ```bash $ xmake f -c checking for LONG_SIZE ... 8 checking for STRING_SIZE ... 24 ``` Alternatively, I can also check in the script domain via `target:check_sizeof`. ### Added Apple XROS platform Apple has added build support for visionOS devices in Xcode15, so we also support it as soon as possible. You only need to execute: ```bash $ xmake f -p applexros $ xmake ``` This will complete the construction of the visionOS/XROS platform. ### Support code merging Finally, we also provide a small tool module that can be used to quickly merge all c/c++ and header file source codes in a specified target into a single source file. A single source code file similar to sqlite3.c will be generated. Users can decide whether to use this function according to their actual needs. When merging, Xmake will expand all internal includes header files and generate DAG, which will be introduced through topological sorting. By default it will handle the merging of all targets, for example: ```bash $ xmake l cli.amalgamate build/tbox.c generated! build/tbox.h generated! ``` We can also specify the targets required for the merge: ```bash $ xmake l cli.amalgamate tbox build/tbox.c generated! build/tbox.h generated! ``` You can also specify a custom unique ID macro definition when merging each source file to handle symbol conflicts. ```bash $ xmake l cli.amalgamate -u MY_UNIQUEU_ID build/tbox.c generated! build/tbox.h generated! ``` If there are symbols with the same name in multiple source files, you can determine whether the `MY_UNIQUEU_ID` macro is defined. If it is defined, it means it is in a single file, and you can handle the symbols with the same name in the source code yourself. ```c #ifdef MY_UNIQUEU_ID //do something #endif ``` We can also specify the output location: ```bash $ xmake l cli.amalgamate -o /xxx /xxx/tbox.c generated! /xxx/tbox.h generated! ``` ### Added windows.manifest.uac policy Through this strategy, we can quickly and easily set up and enable Windows UAC. It supports the following levels: * invoker: asInvoker * admin: requireAdministrator * highest: highestAvailable For example: ```lua set_policy("windows.manifest.uac", "admin") ``` It is equivalent to setting ```lua if is_plat("windows") then add_ldflags("/manifest:embed", {"/manifestuac:level='requireAdministrator' uiAccess='false'"}, {force = true, expand = false}) end ``` But it is more convenient and concise, and there is no need to judge the platform, other platforms are automatically ignored. We can also set the uiAccess of Windows UAC through the `windows.manifest.uac.ui` policy. If it is not set, the default is false. ```lua set_policy("windows.manifest.uac.ui", true) ``` ## Changelog ### New features * [#1452](https://github.com/xmake-io/xmake/issues/1452): Improve link mechanism and order * [#1438](https://github.com/xmake-io/xmake/issues/1438): Support code amalgamation * [#3381](https://github.com/xmake-io/xmake/issues/3381): Add `xmake test` support * [#4276](https://github.com/xmake-io/xmake/issues/4276): Support custom scope api * [#4286](https://github.com/xmake-io/xmake/pull/4286): Add Apple XROS support * [#4345](https://github.com/xmake-io/xmake/issues/4345): Support check sizeof * [#4369](https://github.com/xmake-io/xmake/pull/4369): Add windows.manifest.uac policy ### Changes * [#4284](https://github.com/xmake-io/xmake/issues/4284): Improve builtin includes ### Bugs fixed * [#4256](https://github.com/xmake-io/xmake/issues/4256): Fix intellisense for vsxmake/c++modules --- --- url: /zh/posts/xmake-update-v2.8.5.md --- ## 新特性介绍 在介绍新特性之前,我们有一个好消息要告诉大家,Xmake 最近进入了 Debian 的官方仓库:, 等到明年4月份 Ubuntu 24.04 发布,我们应该就能直接通过 `apt install xmake` 命令去快速安装 Xmake 了。 同时也感谢 @Lance Lin 的帮助,他全程帮助我们维护并上传 Xmake 包到 Debian 仓库,真的非常感谢! 接下来,我们来介绍下 2.8.5 版本引入的一些改动,这个版本带来了很多的新特性,尤其是对链接排序,链接组的支持,还有对 `xmake test` 内置单元测试的支持。 另外,我们还新增了 Apple XROS 平台的构建支持,可以用于构建苹果新的 VisionOS 上的程序,还有我们还提供了更加灵活通用的 `check_sizeof` 检测接口,用于快速检测类型的大小。 ### 链接重排序支持 这是一个存在了两年多的需求,主要用于调整 target 内部的链接顺序。 由于 xmake 提供了 `add_links`, `add_deps`, `add_packages`, `add_options` 接口,可以配置目标、依赖,包和选项中的链接,尽管 `add_links` 本身的链接顺序可以根据添加顺序来调整。 但是 links,deps 和 packages 之间的链接顺序,只能按固定顺序生成,无法灵活调整,这对于一些复杂的项目,就有点显得力不从心了。 而我们在这个版本,彻底解决了这个问题,新增了 `add_linkorders` 接口,可用于配置目标、依赖、包、选项、链接组引入的各种链接顺序。 更多详情和背景,请见:[#1452](https://github.com/xmake-io/xmake/issues/1452) #### 排序链接 为了更加灵活的调整 target 内部的各种链接顺序,我们可以通过 `add_linkorders` 这个新接口来实现,例如: ```lua add_links("a", "b", "c", "d", "e") -- e -> b -> a add_linkorders("e", "b", "a") -- e -> d add_linkorders("e", "d") ``` add\_links 是配置的初始链接顺序,然后我们通过 add\_linkorders 配置了两个局部链接依赖 `e -> b -> a` 和 `e -> d` 后。 xmake 内部就会根据这些配置,生成 DAG 图,通过拓扑排序的方式,生成最终的链接顺序,提供给链接器。 当然,如果存在循环依赖,产生了环,它也会提供警告信息。 #### 排序链接和链接组 另外,对于循环依赖,我们也可以通过 `add_linkgroups` 配置链接组的方式也解决。 并且 `add_linkorders` 也能够对链接组进行排序。 ```lua add_links("a", "b", "c", "d", "e") add_linkgroups("c", "d", {name = "foo", group = true}) add_linkorders("e", "linkgroup::foo") ``` 如果要排序链接组,我们需要对每个链接组取个名,`{name = "foo"}` ,然后就能在 `add_linkorders` 里面通过 `linkgroup::foo` 去引用配置了。 #### 排序链接和frameworks 我们也可以排序链接和 macOS/iPhoneOS 的 frameworks。 ```lua add_links("a", "b", "c", "d", "e") add_frameworks("Foundation", "CoreFoundation") add_linkorders("e", "framework::CoreFoundation") ``` #### 完整例子 相关的完整例子,我们可以看下: ```lua add_rules("mode.debug", "mode.release") add_requires("libpng") target("bar") set_kind("shared") add_files("src/foo.cpp") add_linkgroups("m", "pthread", {whole = true}) target("foo") set_kind("static") add_files("src/foo.cpp") add_packages("libpng", {public = true}) target("demo") set_kind("binary") add_deps("foo") add_files("src/main.cpp") if is_plat("linux", "macosx") then add_syslinks("pthread", "m", "dl") end if is_plat("macosx") then add_frameworks("Foundation", "CoreFoundation") end add_linkorders("framework::Foundation", "png16", "foo") add_linkorders("dl", "linkgroup::syslib") add_linkgroups("m", "pthread", {name = "syslib", group = true}) ``` 完整工程在:[linkorders example](https://github.com/xmake-io/xmake/blob/master/tests/projects/c%2B%2B/linkorders/xmake.lua) ### 链接组支持 另外,这个版本,我们还新增了链接组的原生支持,它目前主要用于 linux 平台的编译,仅支持 gcc/clang 编译器。 需要注意的是 gcc/clang 里面的链接组概念主要特指:`-Wl,--start-group` 而 Xmake 对齐进行了封装,做了进一步抽象,并且不仅仅用于处理 `-Wl,--start-group`,还可以处理 `-Wl,--whole-archive` 和 `-Wl,-Bstatic`。 下面我们会一一对其进行讲解。 更多详情见:[#1452](https://github.com/xmake-io/xmake/issues/1452) #### --start-group 支持 `-Wl,--start-group` 和 `-Wl,--end-group` 是用于处理复杂库依赖关系的链接器选项,确保链接器可以解决符号依赖并成功连接多个库。 在 xmake 中,我们可以通过下面的方式实现: ```lua add_linkgroups("a", "b", {group = true}) ``` 它会对应生成 `-Wl,--start-group -la -lb -Wl,--end-group` 链接选项。 如果 a 和 b 库之间有符号的循环依赖,也不会报链接错误,能够正常链接成功。 对于不支持的平台和编译,会退化成 `-la -lb` #### --whole-archive 支持 `--whole-archive` 是一个链接器选项,通常用于处理静态库。 它的作用是告诉链接器将指定的静态库中的所有目标文件都包含到最终可执行文件中,而不仅仅是满足当前符号依赖的目标文件。 这可以用于确保某些库的所有代码都被链接,即使它们在当前的符号依赖关系中没有直接引用。 更多信息,可以参考 gcc/clang 的文档。 在 xmake 中,我们可以通过下面的方式实现: ```lua add_linkgroups("a", "b", {whole = true}) ``` 它会对应生成 `-Wl,--whole-archive -la -lb -Wl,--no-whole-archive` 链接选项。 对于不支持的平台和编译,会退化成 `-la -lb` 另外,我们可以同时配置 group/whole: ```lua add_linkgroups("a", "b", {whole = true, group = true}) ``` #### -Bstatic 支持 `-Bstatic` 也是用于编译器(如gcc)的选项,用于指示编译器在链接时只使用静态库而不使用共享库。 更多信息,可以参考 gcc/clang 的文档。 在 xmake 中,我们可以通过下面的方式实现: ```lua add_linkgroups("a", "b", {static = true}) ``` 它会对应生成 `-Wl,-Bstatic -la -lb -Wl,-Bdynamic` 链接选项。 ### 单元测试支持 新版本中,我们还增加了一个内置的测试命令:`xmake test`,我们只需要在需要测试的 target 上通过 add\_tests 配置一些测试用例,就可以自动执行测试。 即使当前 target 被设置成了 `set_default(false)`,在执行测试的时候,xmake 也还是会先自动编译它们,然后自动运行所有的测试。 我们可以先看个整体的例子,大概知道下它是怎么样子的。 ```lua add_rules("mode.debug", "mode.release") for _, file in ipairs(os.files("src/test_*.cpp")) do local name = path.basename(file) target(name) set_kind("binary") set_default(false) add_files("src/" .. name .. ".cpp") add_tests("default") add_tests("args", {runargs = {"foo", "bar"}}) add_tests("pass_output", {trim_output = true, runargs = "foo", pass_outputs = "hello foo"}) add_tests("fail_output", {fail_outputs = {"hello2 .*", "hello xmake"}}) end ``` 这个例子,自动扫描源码目录下的 `test_*.cpp` 源文件,然后每个文件自动创建一个测试目标,它被设置成了 `set_default(false)`,也就是正常情况下,默认不会编译它们。 但是,如果执行 `xmake test` 进行测试,它们就会被自动编译,然后测试运行,运行效果如下: ```bash ruki-2:test ruki$ xmake test running tests ... [ 2%]: test_1/args .................................... passed 7.000s [ 5%]: test_1/default .................................... passed 5.000s [ 8%]: test_1/fail_output .................................... passed 5.000s [ 11%]: test_1/pass_output .................................... passed 6.000s [ 13%]: test_2/args .................................... passed 7.000s [ 16%]: test_2/default .................................... passed 6.000s [ 19%]: test_2/fail_output .................................... passed 6.000s [ 22%]: test_2/pass_output .................................... passed 6.000s [ 25%]: test_3/args .................................... passed 7.000s [ 27%]: test_3/default .................................... passed 7.000s [ 30%]: test_3/fail_output .................................... passed 6.000s [ 33%]: test_3/pass_output .................................... passed 6.000s [ 36%]: test_4/args .................................... passed 6.000s [ 38%]: test_4/default .................................... passed 6.000s [ 41%]: test_4/fail_output .................................... passed 5.000s [ 44%]: test_4/pass_output .................................... passed 6.000s [ 47%]: test_5/args .................................... passed 5.000s [ 50%]: test_5/default .................................... passed 6.000s [ 52%]: test_5/fail_output .................................... failed 6.000s [ 55%]: test_5/pass_output .................................... failed 5.000s [ 58%]: test_6/args .................................... passed 7.000s [ 61%]: test_6/default .................................... passed 6.000s [ 63%]: test_6/fail_output .................................... passed 6.000s [ 66%]: test_6/pass_output .................................... passed 6.000s [ 69%]: test_7/args .................................... failed 6.000s [ 72%]: test_7/default .................................... failed 7.000s [ 75%]: test_7/fail_output .................................... failed 6.000s [ 77%]: test_7/pass_output .................................... failed 5.000s [ 80%]: test_8/args .................................... passed 7.000s [ 83%]: test_8/default .................................... passed 6.000s [ 86%]: test_8/fail_output .................................... passed 6.000s [ 88%]: test_8/pass_output .................................... failed 5.000s [ 91%]: test_9/args .................................... passed 6.000s [ 94%]: test_9/default .................................... passed 6.000s [ 97%]: test_9/fail_output .................................... passed 6.000s [100%]: test_9/pass_output .................................... passed 6.000s 80% tests passed, 7 tests failed out of 36, spent 0.242s ``` ![](/assets/img/manual/xmake-test1.png) 我们也可以执行 `xmake test -vD` 查看详细的测试失败的错误信息: ![](/assets/img/manual/xmake-test2.png) #### 运行指定测试目标 我们也可以指定运行指定 target 的某个测试: ```bash $ xmake test targetname/testname ``` 或者按模式匹配的方式,运行一个 target 的所有测试,或者一批测试: ```bash $ xmake test targetname/* $ xmake test targetname/foo* ``` 也可以运行所有 target 的同名测试: ```bash $ xmake test */testname ``` #### 并行化运行测试 其实,默认就是并行化运行的,但是我们可以通过 `-jN` 调整运行的并行度。 ```bash $ xmake test -jN ``` #### 分组运行测试 ```bash $ xmake test -g "foo" $ xmake test -g "foo*" ``` #### 添加测试到目标(无参数) 如果没有配置任何参数,仅仅配置了测试名到 `add_tests`,那么仅仅测试这个目标程序的是否会运行失败,根据退出代码来判断是否通过测试。 ``` target("test") add_tests("testname") ``` #### 配置运行参数 我们也可以通过 `{runargs = {"arg1", "arg2"}}` 的方式,给 `add_tests` 配置指定测试需要运行的参数。 另外,一个 target 可以同时配置多个测试用例,每个测试用例可独立运行,互不冲突。 ```lua target("test") add_tests("testname", {runargs = "arg1"}) add_tests("testname", {runargs = {"arg1", "arg2"}}) ``` 如果我们没有配置 runargs 到 `add_tests`,那么我们也会尝试从被绑定的 target 中,获取 `set_runargs` 设置的运行参数。 ```lua target("test") add_tests("testname") set_runargs("arg1", "arg2") ``` #### 配置运行目录 我们也可以通过 rundir 设置测试运行的当前工作目录,例如: ```lua target("test") add_tests("testname", {rundir = os.projectdir()}) ``` 如果我们没有配置 rundir 到 `add_tests`,那么我们也会尝试从被绑定的 target 中,获取 `set_rundir` 设置的运行目录。 ```lua target("test") add_tests("testname") set_rundir("$(projectdir)") ``` #### 配置运行环境 我们也可以通过 runenvs 设置一些运行时候的环境变量,例如: ```lua target("test") add_tests("testname", {runenvs = {LD_LIBRARY_PATH = "/lib"}}) ``` 如果我们没有配置 runenvs 到 `add_tests`,那么我们也会尝试从被绑定的 target 中,获取 `add_runenvs` 设置的运行环境。 ```lua target("test") add_tests("testname") add_runenvs("LD_LIBRARY_PATH", "/lib") ``` #### 匹配输出结果 默认情况下,`xmake test` 会根据测试运行的退出代码是否为 0,来判断是否测试通过。 当然,我们也可以通过配置测试运行的输出结果是否满足我们的指定的匹配模式,来判断是否测试通过。 主要通过这两个参数控制: | 参数 | 说明 | | --- | --- | | pass\_outputs | 如果输出匹配,则测试通过 | | fail\_outputs | 如果输出匹配,则测试失败 | 传入 `pass_outputs` 和 `fail_outputs` 的是一个 lua 匹配模式的列表,但模式稍微做了一些简化,比如对 `*` 的处理。 如果要匹配成功,则测试通过,可以这么配置: ```lua target("test") add_tests("testname1", {pass_outputs = "hello"}) add_tests("testname2", {pass_outputs = "hello *"}) add_tests("testname3", {pass_outputs = {"hello", "hello *"}}) ``` 如果要匹配成功,则测试失败,可以这么配置: ```lua target("test") add_tests("testname1", {fail_outputs = "hello"}) add_tests("testname2", {fail_outputs = "hello *"}) add_tests("testname3", {fail_outputs = {"hello", "hello *"}}) ``` 我们也可以同时配置它们: ```lua target("test") add_tests("testname", {pass_outputs = "foo", fail_outputs = "hello"}) ``` 由于一些测试输出的结果,尾部会有一些换行什么的空白字符,干扰匹配模式,我们可以再配置 `trim_output = true`,先截断空白字符后,再做匹配。 ```lua target("test") add_tests("testname", {trim_output = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` 我们还可以配置 `{plain = true}` 是禁用 lua 模式匹配,仅仅做最基础的平坦文本匹配。 ```lua target("test") add_tests("testname", {plain = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` #### 配置测试组 我们也可以通过 `group = "foo"` 来配置一个测试组,进行分组测试: ```lua target("test") add_tests("testname1", {group = "foo"}) add_tests("testname2", {group = "foo"}) add_tests("testname3", {group = "bar"}) add_tests("testname4", {group = "bae"}) ``` 其中 testname1/testname2 是一个组 foo,另外两个是在另外一个组。 然后,我们就可以使用 `xmake test -g groupname` 来进行分组测试了。 ```bash $ xmake test -g "foo" $ xmake test -g "foo*" ``` :::注意 运行分组,也是支持模式匹配的。 ::: 另外,如果没有设置 `group` 参数给 `add_tests`,我们也可以默认获取绑定到 target 的组名。 ```lua target("test") add_tests("testname") set_group("foo") ``` #### 自定义测试脚本 我们还新增了 `before_test`, `on_test` 和 `after_test` 配置脚本,用户可以在 rule 和 target 域,自定义配置它们实现定制化的测试执行。 ```lua target("test") on_test(function (target, opt) print(opt.name, opt.runenvs, opt.runargs, opt.pass_outputs) -- do test -- ... -- passed return true -- failied return false, errors end) ``` 其中,opt 里面可以获取到所有传入 `add_tests` 的参数,我们在 on\_test 里面自定义测试逻辑,然后返回 true 就是测试通过,返回 false 就是测试失败,然后继续返回测试失败的错误信息。 #### 自动化构建 由于测试目标在正常开发构建阶段,通常是不需要被构建的,因此我们会设置 `set_default(false)`。 ```lua target("test") add_tests("testname") set_default(false) ``` 但是运行 `xmake test` 进行测试时候,这些测试对应的 target 还是会被自动构建,确保能够被运行。 ```bash $ xmake test [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test running tests ... [100%]: test/testname .................................... passed 6.000s 100% tests passed, 0 tests failed out of 1, spent 0.006s ``` #### 首次测试失败就终止 默认情况下,`xmake test` 会等到所有测试都运行完,不管里面有多少是没通过的。 而有时候,我们想在第一个测试没通过,就直接中断测试,那么我们可以通过下面的配置启用: ```lua set_policy("test.return_zero_on_failure", true) ``` #### 测试失败返回0 默认情况下,只要有一个测试没通过,等到 `xmake test` 运行完成,它都会返回非0退出代码,这对于一些 CI 环境非常有用,可以中断 CI 的其他脚本继续运行。 然后触发信号告诉 CI,我们需要生成测试报告和告警了。 然后,如果我们想要压制这种行为,可以强制将 `xmake test` 的退出代码总是设置成 0。 ```lua set_policy("test.return_zero_on_failure", true) ``` #### 仅仅测试编译 有时候,我们仅仅想要测试代码是否通过编译,或者没有通过编译,不需要运行它们,那么可以通过配置 `build_should_pass` 和 `build_should_fail` 来实现。 ```lua target("test_10") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_fail", {build_should_fail = true}) target("test_11") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_pass", {build_should_pass = true}) ``` 这通常用于一些测试代码中带有 `static_assert` 的场景,例如: ```c++ template bool foo(T val) { if constexpr (std::is_same_v) { printf("int!\n"); } else if constexpr (std::is_same_v) { printf("float!\n"); } else { static_assert(false, "unsupported type"); } } int main(int, char**) { foo("BAD"); return 0; } ``` #### 配置额外的代码编译 我们还可以在配置测试用例的时候,对每个测试配置额外需要编译的代码,以及一些宏定义,实现内联测试。 xmake 会为每个测试单独编译一个独立的可执行程序去运行它,但这并不会影响到 target 在生产环境的编译结果。 ```lua target("test_13") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_1", {files = "tests/stub_1.cpp", defines = "STUB_1"}) target("test_14") set_kind("binary") set_default(false) add_files("src/test_2.cpp") add_tests("stub_2", {files = "tests/stub_2.cpp", defines = "STUB_2"}) target("test_15") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_n", {files = "tests/stub_n*.cpp", defines = "STUB_N"}) ``` 以 doctest 为例,我们可以在不修改任何 main.cpp 的情况下,外置单元测试: ```lua add_rules("mode.debug", "mode.release") add_requires("doctest") target("doctest") set_kind("binary") add_files("src/*.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { files = testfile, remove_files = "src/main.cpp", languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` 定义 DOCTEST\_CONFIG\_IMPLEMENT\_WITH\_MAIN 会引入额外的 main 入口函数,因此我们需要配置 remove\_files 去移除已有的 main.cpp 文件。 运行效果如下: ```bash ruki-2:doctest ruki$ xmake test running tests ... [ 50%]: doctest/test_1 .................................... failed 0.009s [100%]: doctest/test_2 .................................... passed 0.009s 50% tests passed, 1 tests failed out of 2, spent 0.019s ruki-2:doctest ruki$ xmake test -v running tests ... [ 50%]: doctest/test_1 .................................... failed 0.026s [doctest] doctest version is "2.4.11" [doctest] run with "--help" for options =============================================================================== tests/test_1.cpp:7: TEST CASE: testing the factorial function tests/test_1.cpp:8: ERROR: CHECK( factorial(1) == 10 ) is NOT correct! values: CHECK( 1 == 10 ) =============================================================================== [doctest] test cases: 1 | 0 passed | 1 failed | 0 skipped [doctest] assertions: 4 | 3 passed | 1 failed | [doctest] Status: FAILURE! run failed, exit code: 1 [100%]: doctest/test_2 .................................... passed 0.010s 50% tests passed, 1 tests failed out of 2, spent 0.038s ``` #### 测试动态库 通常,`add_tests` 仅用于对可执行程序进行运行测试,运行动态库需要有一个额外的 main 主入口,因此我们需要额外配置一个可执行程序去加载它,例如: ```lua target("doctest_shared") set_kind("shared") add_files("src/foo.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { kind = "binary", files = testfile, languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` 通过 `kind = "binary"` 可以将每个单元测试改为 binary 可执行程序,并通过 `DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN` 引入 main 入口函数。 这样就能实现动态库目标中外置可运行的单元测试。 ### 新增类型大小检测 在先前的版本中,我们可以通过 `check_csnippets` 和 `output = true` 的方式,来实现类型检测。 ```lua check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` 但是这种方式,是通过尝试运行测试代码,然后获取运行输出结果,提取类型大小信息。 这对于交叉编译,就不适用了。 在 2.8.5 版本中,我们新增了 `check_sizeof` 辅助接口,可以通过直接解析测试程序的二进制文件,提取类型大小信息。 由于不需要运行测试,这种方式不仅可以支持交叉编译,而且对检测效率也有极大的提升,使用也更加的简单。 ```lua includes("@builtin/check") target("test") set_kind("static") add_files("*.cpp") check_sizeof("LONG_SIZE", "long") check_sizeof("STRING_SIZE", "std::string", {includes = "string"}) ``` ```bash $ xmake f -c checking for LONG_SIZE ... 8 checking for STRING_SIZE ... 24 ``` 另外,我也可以通过 `target:check_sizeof` 在脚本域进行检测。 ### 新增 Apple XROS 平台 苹果在 Xcode15 中新增了 visionOS 设备的构建支持,因此我们也在第一时间对其进行了支持,只需要执行: ```bash $ xmake f -p applexros $ xmake ``` 就可以完成 visionOS/XROS 平台的构建。 ### 支持代码合并 最后,我们还提供了一个小工具模块,它可以用于快速合并指定 target 里面的所有 c/c++ 和 头文件源码到单个源文件。 会生成类似 sqlite3.c 的这种单源码文件,用户可以根据自己的实际需求来决定是否使用这个功能。 而在做合并的时候,Xmake 会将内部 includes 头文件全部展开,并生成 DAG,通过拓扑排序引入。 默认它会处理所有 target 的合并,例如: ```bash $ xmake l cli.amalgamate build/tbox.c generated! build/tbox.h generated! ``` 我们也可以指定合并需要的目标: ```bash $ xmake l cli.amalgamate tbox build/tbox.c generated! build/tbox.h generated! ``` 也可以在合并每个源文件时候,指定一个自定义的 unique ID 的宏定义,来处理符号冲突问题。 ```bash $ xmake l cli.amalgamate -u MY_UNIQUEU_ID build/tbox.c generated! build/tbox.h generated! ``` 如果多个源文件内部有重名符号,就可以判断这个 `MY_UNIQUEU_ID` 宏是否被定义,如果定义了,说明是在单文件中,就自己在源码中处理下重名符号。 ```c #ifdef MY_UNIQUEU_ID // do some thing #endif ``` 我们也可以指定输出位置: ```bash $ xmake l cli.amalgamate -o /xxx /xxx/tbox.c generated! /xxx/tbox.h generated! ``` ### 新增 windows.manifest.uac 策略 通过这个策略,我们可以快速方便的设置并启用 Windows UAC。 它支持以下几个 Level: * invoker: asInvoker * admin: requireAdministrator * highest: highestAvailable 例如: ```lua set_policy("windows.manifest.uac", "admin") ``` 它等价于设置 ```lua if is_plat("windows") then add_ldflags("/manifest:embed", {"/manifestuac:level='requireAdministrator' uiAccess='false'"}, {force = true, expand = false}) end ``` 但是更加方便简洁,并且不需要判断平台,其他平台自动忽略。 我们也可以通过 `windows.manifest.uac.ui` 策略,设置 Windows UAC 的 uiAccess,如果没有设置它,默认是 false。 ```lua set_policy("windows.manifest.uac.ui", true) ``` ## 更新日志 ### 新特性 * [#1452](https://github.com/xmake-io/xmake/issues/1452): 支持链接顺序调整,链接组 * [#1438](https://github.com/xmake-io/xmake/issues/1438): 支持代码 amalgamation * [#3381](https://github.com/xmake-io/xmake/issues/3381): 添加 `xmake test` 支持 * [#4276](https://github.com/xmake-io/xmake/issues/4276): 支持自定义域 API * [#4286](https://github.com/xmake-io/xmake/pull/4286): 添加 Apple XROS 支持 * [#4345](https://github.com/xmake-io/xmake/issues/4345): 支持检测类型大小 sizeof * [#4369](https://github.com/xmake-io/xmake/pull/4369): 添加 windows.manifest.uac 策略 ### 改进 * [#4284](https://github.com/xmake-io/xmake/issues/4284): 改进内置 includes 模块 ### Bugs 修复 * [#4256](https://github.com/xmake-io/xmake/issues/4256): 为 vsxmake 生成器修复 c++ modules intellisense --- --- url: /posts/xmake-update-v2.8.6.md --- ## Introduction of new features Before introducing new features, there is good news to tell you that the previous version of Xmake was included in the debian repository, and recently Xmake has entered the Fedora official repository. You can install Xmake directly on Fedora 39 through the following command. ```bash $ sudo dnf install xmake ``` Many thanks to @topazus @mochaaP for their contribution to Xmake. For related information, see: [#941](https://github.com/xmake-io/xmake/issues/941). Next, let’s introduce the heavyweight feature brought by the new version: XPack. It is similar to CMake's CPack command, which can quickly package user projects to generate installation packages in various formats. Currently Xmake's XPack already supports packaging in the following formats: * nsis: executable installation package under Windows * runself: shell self-compiled installation package * targz: binary file tar.gz package (green version) * zip: Binary file zip package (green version) * srctargz: source file tar.gz package * srczip: source file zip package * srpm: rpm source code installation package * rpm: rpm binary installation package In addition to the above-mentioned supported packaging formats, package formats such as deb are also being gradually supported, and users can also configure and generate custom package format files. ### XPack packaging Here is a complete example, we can take a brief look at it first: ```lua set_version("1.0.0") add_rules("mode.debug", "mode.release") includes("@builtin/xpack") target("test") set_kind("binary") add_files("src/*.cpp") xpack("test") set_formats("nsis", "zip", "targz", "runself") set_title("hello") set_author("ruki") set_description("A test installer.") set_homepage("https://xmake.io") set_licensefile("LICENSE.md") add_targets("test") add_installfiles("src/(assets/*.png)", {prefixdir = "images"}) add_sourcefiles("(src/**)") set_iconfile("src/assets/xmake.ico") after_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) after_uninstallcmd(function (package, batchcmds) batchcmds:rmdir(package:installdir("resources")) batchcmds:rmdir(package:installdir("stub")) end) ``` We introduce all configuration interfaces of xpack through `includes("@builtin/xpack")`, including the xpack configuration domain and all its domain interfaces. Then we execute: ```bash $xmakepack ``` All installation packages will be generated. ### Generate NSIS installation package As long as you configure the `set_formats("nsis")` format and then execute the `xmake pack` command, you can generate an installation package in NSIS format. In addition, xmake will also automatically install the tools required to generate NSIS packages to achieve true one-click packaging. ```bash $xmakepack note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> nsis 3.09 please input: y (y/n/m) => install nsis 3.09 .. ok [25%]: compiling.release src\main.cpp [37%]: compiling.release src\main.cpp [50%]: linking.release foo.dll [62%]: linking.release test.exe packing build\xpack\test\test-windows-x64-v1.0.0.exe pack ok ``` `test-windows-x64-v1.0.0.exe` is the installation package we generated. Double-click to run it to install our binary files to the specified directory. ![](/assets/img/manual/nsis_1.png) ![](/assets/img/manual/nsis_2.png) ![](/assets/img/manual/nsis_3.png) #### Add component installation We can also add component installation commands to NSIS. Only when the user selects the specified component, its installation command will be executed. ```lua xpack("test") add_components("LongPath") xpack_component("LongPath") set_default(false) set_title("Enable Long Path") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") on_installcmd(function (component, batchcmds) batchcmds:rawcmd("nsis", [[ ${If} $NoAdmin == "false" ; Enable long path WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ${EndIf}]]) end) ``` In this example, we added an NSIS-specific custom command to support long paths. ![](/assets/img/manual/nsis_4.png) ### Generate self-installation package We can also generate self-compiled installation packages based on shell scripts. We need to configure the runself packaging format, and then add the source files that need to participate in compilation and installation through `add_sourcefiles`. Next, we need to customize the on\_installcmd installation script to configure if we compile the source code package, we can simply call a built-in compilation and installation script file, or directly configure compilation and installation commands such as `make install`. For example: ```lua xpack("test") set_formats("runself") add_sourcefiles("(src/**)") on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install"}) end) ``` Then, we execute the `xmake pack` command to generate a self-installed xxx.gz.run package, which uses gzip compression by default. ```bash $xmakepack packing build/xpack/test/test-macosx-src-v1.0.0.gz.run pack ok ``` We can use sh to load and run it to install our program. ```bash $ sh ./build/xpack/test/test-macosx-src-v1.0.0.gz.run ``` We can also look at a more complete example: ```lua xpack("xmakesrc") set_formats("runself") set_basename("xmake-v$(version)") set_prefixdir("xmake-$(version)") before_package(function (package) import("devel.git") local rootdir = path.join(os.tmpfile(package:basename()) .. ".dir", "repo") if not os.isdir(rootdir) then os.tryrm(rootdir) os.cp(path.directory(os.projectdir()), rootdir) git.clean({repodir = rootdir, force = true, all = true}) git.reset({repodir = rootdir, hard = true}) if os.isfile(path.join(rootdir, ".gitmodules")) then git.submodule.clean({repodir = rootdir, force = true, all = true}) git.submodule.reset({repodir = rootdir, hard = true}) end end local extraconf = {rootdir = rootdir} package:add("sourcefiles", path.join(rootdir, "core/**|src/pdcurses/**|src/luajit/**|src/tbox/tbox/src/demo/**"), extraconf ) package:add("sourcefiles", path.join(rootdir, "xmake/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "*.md"), extraconf) package:add("sourcefiles", path.join(rootdir, "configure"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/*.sh"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/man/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/debian/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/msys/**"), extraconf) end) on_installcmd(function (package, batchcmds) batchcmds:runv("./scripts/get.sh", {"__local__"}) end) ``` It is the installation package configuration script of xmake's own source code. For more complete configuration, please refer to: [xpack.lua](https://github.com/xmake-io/xmake/blob/master/core/xpack.lua) Here, it performs compilation and installation by calling the `./scripts/get.sh` installation script built into the source package. ### Generate source code archive package In addition, we can also configure the `srczip` and `srctargz` formats to generate source code compression packages. It is not a complete installation package and has no installation commands. It is only used for source code package distribution. ```lua xpack("test") set_formats("srczip", "srctargz") add_sourcefiles("(src/**)") ``` ```bash $xmakepack packing build/xpack/test/test-macosx-src-v1.0.0.zip .. packing build/xpack/test/test-macosx-src-v1.0.0.tar.gz .. pack ok ``` ### Generate binary archive package We can also configure `zip` and `targz` to generate binary compressed packages. It will automatically compile all bound target target programs and package all required binary programs and library files into zip/tar.gz format. This is usually used to create a green version of the installation package. There is no automatic installation script inside. Users need to set environment variables such as PATH themselves. ```lua xpack("test") set_formats("zip", "targz") add_installfiles("(src/**)") ``` ```bash $xmakepack packing build/xpack/test/test-macosx-v1.0.0.zip .. packing build/xpack/test/test-macosx-v1.0.0.tar.gz .. pack ok ``` :::NOTE It should be noted that to add binary files to the package, use `add_installfiles` instead of `add_sourcefiles`. ::: We can also use `add_targets` to bind the target target programs and libraries that need to be installed. See the interface description for `add_targets` below for more details. ### Generate SRPM source code installation package It can generate source code installation packages in `.src.rpm` format. We can configure add\_targets to associate the targets that need to be built. In the generated srpm package, it will automatically call `xmake build` and `xmake install` to build and install the package. ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./xmake.lua") add_targets("demo") ``` It will generate a spec file similar to the following, and then automatically call rpmbuild to generate the `.src.rpm` package. ``` Name: test Version: 1.0.0 Release: 1%{?dist} Summary: hello License: Apache-2.0 URL: https://xmake.io Source0: test-linux-src-v1.0.0.tar.gz BuildRequires: xmake BuildRequires: gcc BuildRequires: gcc-c++ %description A test installer. %prep %autosetup -n test-1.0.0 -p1 %build xmake build -y test %install xmake install -o %{buildroot}/%{_exec_prefix} test cd %{buildroot} find . -type f | sed 's!^\./!/!' > %{_builddir}/_installedfiles.txt %check %files -f %{_builddir}/_installedfiles.txt %changelog * Fri Dec 22 2023 ruki - 1.0.0-1 - Update to 1.0.0 ``` We can also customize build and installation scripts through `on_buildcmd` and `on_installcmd`. ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./configure") on_buildcmd(function (package, batchcmds) batchcmds:runv("./configure") batchcmds:runv("make") end) on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install", "PREFIX=%{buildroot}"}) end) ``` ### Generate RPM binary installation package The RPM package will directly generate a compiled binary installation package. xmake will automatically call the `rpmbuild --rebuild` command to build the SRPM package and generate it. In XPack, we only need to configure `set_formats("rpm")` to support rpm package generation, and other configurations are exactly the same as srpm packages. ```lua xpack("test") set_formats("rpm") -- TODO ``` ### Packaging command parameters #### Specify packaging format If we have configured multiple packaging formats using `set_formats` in the configuration file, then `xmake pack` will automatically generate packages for all these formats by default. Of course, we can also use `xmake pack --formats=nsis,targz` to selectively specify which formats of packages currently need to be packaged. #### Modify the package file name We can modify the package name through `set_basename()` in the configuration file, or we can modify it through the command line. ```bash $ xmake pack --basename="foo" packing build/xpack/test/foo.zip .. pack ok ``` #### Specify output directory The default output directory is in the build directory, but we can also modify the output path. ```bash $ xmake pack -o /tmp/output ``` #### Disable automatic build If you are building a binary package such as NSIS, `xmake pack` will automatically compile all bound target files first, and then execute the packaging logic. But if we have already compiled it and don't want to compile it every time, but package it directly, we can disable automatic building through the following parameters. ```bash $ xmake pack --autobuild=n ``` ### Interface description For more descriptions of the XPack packaging interface, see: [XPack Packaging Interface Document](https://xmake.io). ### Install the package locally By default, packages configured through `add_requires("xxx")` will be installed in the global directory, and different projects share these packages. In the new version, we have added a `package.install_locally` strategy, which can be configured to let xmake install the package to the current local project directory. ```lua set_policy("package.install_locally", true) ``` ## Changelog ### New features * Add `network.mode` policy * [#1433](https://github.com/xmake-io/xmake/issues/1433): Add `xmake pack` command to generate NSIS/zip/tar.gz/rpm/srpm/runself packages like cmake/cpack * [#4435](https://github.com/xmake-io/xmake/issues/4435): Support batchsize for UnityBuild in Group Mode * [#4485](https://github.com/xmake-io/xmake/pull/4485): Support package.install\_locally * Support NetBSD ### Changes * [#4484](https://github.com/xmake-io/xmake/pull/4484): Improve swig rule * Improve Haiku support ### Bugs fixed * [#4372](https://github.com/xmake-io/xmake/issues/4372): Fix protobuf rules * [#4439](https://github.com/xmake-io/xmake/issues/4439): Fix asn1c rules --- --- url: /zh/posts/xmake-update-v2.8.6.md --- ## 新特性介绍 在介绍新特性之前,还有个好消息告诉大家,上个版本 Xmake 被收入到了 debian 仓库,而最近 Xmake 又进入了 Fedora 官方仓库,大家可以在 Fedora 39 上,直接通过下面的命令安装 Xmake。 ```bash $ sudo dnf install xmake ``` 非常感谢 @topazus @mochaaP 对 Xmake 的贡献,相关信息见:[#941](https://github.com/xmake-io/xmake/issues/941)。 接下来,我们来介绍下,新版本带来的重量级特性:XPack。 它类似于 CMake 的 CPack 命令,可以将用户工程快速打包生成各种格式的安装包。 目前 Xmake 的 XPack 已经支持以下格式的打包: * nsis: Windows 下的可执行安装包 * runself: shell 自编译安装包 * targz: 二进制文件 tar.gz 包(绿色版) * zip: 二进制文件 zip 包(绿色版) * srctargz:源文件 tar.gz 包 * srczip: 源文件 zip 包 * srpm: rpm 源码安装包 * rpm: rpm 二进制安装包 除了上述已经支持的打包格式,还有 deb 等包格式也在陆续支持中,并且用户也可以配置生成自定义的包格式文件。 ### XPack 打包 下面是一个完整例子,我们可以先简单看下: ```lua set_version("1.0.0") add_rules("mode.debug", "mode.release") includes("@builtin/xpack") target("test") set_kind("binary") add_files("src/*.cpp") xpack("test") set_formats("nsis", "zip", "targz", "runself") set_title("hello") set_author("ruki") set_description("A test installer.") set_homepage("https://xmake.io") set_licensefile("LICENSE.md") add_targets("test") add_installfiles("src/(assets/*.png)", {prefixdir = "images"}) add_sourcefiles("(src/**)") set_iconfile("src/assets/xmake.ico") after_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) after_uninstallcmd(function (package, batchcmds) batchcmds:rmdir(package:installdir("resources")) batchcmds:rmdir(package:installdir("stub")) end) ``` 我们通过 `includes("@builtin/xpack")` 引入 xpack 的所有配置接口,包括 xpack 配置域,以及它的所有域接口。 然后我们执行: ```bash $ xmake pack ``` 即可生成所有安装包。 ### 生成 NSIS 安装包 只要配置了 `set_formats("nsis")` 格式,然后执行 `xmake pack` 命令,就能生成 NSIS 格式的安装包。 另外,xmake 还会自动安装生成 NSIS 包所需的工具,实现真正的一键打包。 ```bash $ xmake pack note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> nsis 3.09 please input: y (y/n/m) => install nsis 3.09 .. ok [ 25%]: compiling.release src\main.cpp [ 37%]: compiling.release src\main.cpp [ 50%]: linking.release foo.dll [ 62%]: linking.release test.exe packing build\xpack\test\test-windows-x64-v1.0.0.exe pack ok ``` `test-windows-x64-v1.0.0.exe` 就是我们生成的安装包,双击运行它,就能安装我们的二进制文件到指定目录。 ![](/assets/img/manual/nsis_1.png) ![](/assets/img/manual/nsis_2.png) ![](/assets/img/manual/nsis_3.png) #### 增加组件安装 我们还可以给 NSIS 增加组件安装命令,只有当用户选择指定组件的时候,它的安装命令才会被执行。 ```lua xpack("test") add_components("LongPath") xpack_component("LongPath") set_default(false) set_title("Enable Long Path") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") on_installcmd(function (component, batchcmds) batchcmds:rawcmd("nsis", [[ ${If} $NoAdmin == "false" ; Enable long path WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ${EndIf}]]) end) ``` 这个例子中,我们在里面添加了一个 NSIS 特有的自定义命令,去实现对长路径的支持。 ![](/assets/img/manual/nsis_4.png) ### 生成自安装包 我们也可以生成基于 shell 脚本的自编译安装包。我们需要配置 runself 打包格式,然后通过 `add_sourcefiles` 添加需要参与编译安装的源文件。 接着,我们需要自定义 on\_installcmd 安装脚本,里面去配置如果编译源码包,我们可以简单的调用一个内置的编译安装脚本文件,也可以直接配置 `make install` 等编译安装命令。 例如: ```lua xpack("test") set_formats("runself") add_sourcefiles("(src/**)") on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install"}) end) ``` 然后,我们执行 `xmake pack` 命令,就可以生成一个自安装的 xxx.gz.run 包,默认采用 gzip 压缩。 ```bash $ xmake pack packing build/xpack/test/test-macosx-src-v1.0.0.gz.run pack ok ``` 我们可以使用 sh 去加载运行它来安装我们的程序。 ```bash $ sh ./build/xpack/test/test-macosx-src-v1.0.0.gz.run ``` 我们也可以看一个比较完整的例子: ```lua xpack("xmakesrc") set_formats("runself") set_basename("xmake-v$(version)") set_prefixdir("xmake-$(version)") before_package(function (package) import("devel.git") local rootdir = path.join(os.tmpfile(package:basename()) .. ".dir", "repo") if not os.isdir(rootdir) then os.tryrm(rootdir) os.cp(path.directory(os.projectdir()), rootdir) git.clean({repodir = rootdir, force = true, all = true}) git.reset({repodir = rootdir, hard = true}) if os.isfile(path.join(rootdir, ".gitmodules")) then git.submodule.clean({repodir = rootdir, force = true, all = true}) git.submodule.reset({repodir = rootdir, hard = true}) end end local extraconf = {rootdir = rootdir} package:add("sourcefiles", path.join(rootdir, "core/**|src/pdcurses/**|src/luajit/**|src/tbox/tbox/src/demo/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "xmake/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "*.md"), extraconf) package:add("sourcefiles", path.join(rootdir, "configure"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/*.sh"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/man/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/debian/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/msys/**"), extraconf) end) on_installcmd(function (package, batchcmds) batchcmds:runv("./scripts/get.sh", {"__local__"}) end) ``` 它是 xmake 自身源码的安装包配置脚本,更完整的配置可以参考:[xpack.lua](https://github.com/xmake-io/xmake/blob/master/core/xpack.lua) 这里,它通过调用源码包内置的 `./scripts/get.sh` 安装脚本去执行编译安装。 ### 生成源码归档包 另外,我们也可以配置 `srczip` 和 `srctargz` 格式,来生成源码压缩包,它不是完整的安装包,也没有安装命令,仅仅用于源码包分发。 ```lua xpack("test") set_formats("srczip", "srctargz") add_sourcefiles("(src/**)") ``` ```bash $ xmake pack packing build/xpack/test/test-macosx-src-v1.0.0.zip .. packing build/xpack/test/test-macosx-src-v1.0.0.tar.gz .. pack ok ``` ### 生成二进制归档包 我们也可以配置 `zip` 和 `targz` 来生成二进制的压缩包,它会先自动编译所有绑定的 target 目标程序,将所有需要的二进制程序,库文件打包到 zip/tar.gz 格式。 这通常用于制作绿色版的安装包,内部不太任何自动安装脚本,用户需要自己设置 PATH 等环境变量。 ```lua xpack("test") set_formats("zip", "targz") add_installfiles("(src/**)") ``` ```bash $ xmake pack packing build/xpack/test/test-macosx-v1.0.0.zip .. packing build/xpack/test/test-macosx-v1.0.0.tar.gz .. pack ok ``` :::注意 需要注意的是,打二进制文件到包里,使用的是 `add_installfiles` 而不是 `add_sourcefiles`。 ::: 我们也可以通过 `add_targets` 去绑定需要安装的 target 目标程序和库。更多详情见下面关于 `add_targets` 的接口描述。 ### 生成 SRPM 源码安装包 它可以生成 `.src.rpm` 格式的源码安装包。 我们可以通过配置 add\_targets 关联需要构建的目标,在生成的 srpm 包中,它会自动调用 `xmake build` 和 `xmake install` 去构建和安装包。 ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./xmake.lua") add_targets("demo") ``` 它会生成类似下面的 spec 文件,然后自动调用 rpmbuild 去生成 `.src.rpm` 包。 ``` Name: test Version: 1.0.0 Release: 1%{?dist} Summary: hello License: Apache-2.0 URL: https://xmake.io Source0: test-linux-src-v1.0.0.tar.gz BuildRequires: xmake BuildRequires: gcc BuildRequires: gcc-c++ %description A test installer. %prep %autosetup -n test-1.0.0 -p1 %build xmake build -y test %install xmake install -o %{buildroot}/%{_exec_prefix} test cd %{buildroot} find . -type f | sed 's!^\./!/!' > %{_builddir}/_installedfiles.txt %check %files -f %{_builddir}/_installedfiles.txt %changelog * Fri Dec 22 2023 ruki - 1.0.0-1 - Update to 1.0.0 ``` 我们也可以通过 `on_buildcmd` 和 `on_installcmd` 自定义构建和安装脚本。 ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./configure") on_buildcmd(function (package, batchcmds) batchcmds:runv("./configure") batchcmds:runv("make") end) on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install", "PREFIX=%{buildroot}"}) end) ``` ### 生成 RPM 二进制安装包 RPM 包将会直接生成编译好的二进制安装包。xmake 会自动调用 `rpmbuild --rebuild` 命令去构建 SRPM 包生成它。 而在 XPack 中,我们仅仅只需要配置 `set_formats("rpm")` 即可支持 rpm 包生成,其他配置与 srpm 包完全一致。 ```lua xpack("test") set_formats("rpm") -- TODO ``` ### 打包命令参数 #### 指定打包格式 如果我们在配置文件中已经使用 `set_formats` 配置了多个打包格式,那么默认情况下,`xmake pack` 会自动生成所有这些格式的包。 当然,我们也可以通过 `xmake pack --formats=nsis,targz` 来选择性指定当前需要打哪些格式的包。 #### 修改打包文件名 我们可以在配置文件中,通过 `set_basename()` 来修改包名,也可以通过命令行去修改它。 ```bash $ xmake pack --basename="foo" packing build/xpack/test/foo.zip .. pack ok ``` #### 指定输出目录 默认的输出目录是在 build 目录下,但我们也可以修改输出的路径。 ```bash $ xmake pack -o /tmp/output ``` #### 禁用自动构建 如果是打 NSIS 等二进制包,`xmake pack` 会先自动编译所有被绑定的 target 目标文件,然后再去执行打包逻辑。 但是如果我们已经编译过了,不想每次都去编译它,而是直接去打包,可以通过下面的参数禁用自动构建。 ```bash $ xmake pack --autobuild=n ``` ### 接口描述 更多 XPack 打包接口描述见:[XPack 打包接口文档](https://xmake.io/zh/)。 ### 安装包到本地 默认情况先,通过 `add_requires("xxx")` 配置的包都会被安装到全局目录,不同项目共用这些包。 而新版本中,我们新增了一个 `package.install_locally` 策略,可以配置让 xmake 将包安装到当前本地项目目录。 ```lua set_policy("package.install_locally", true) ``` ## 更新日志 ### 新特性 * 添加 `network.mode` 策略 * [#1433](https://github.com/xmake-io/xmake/issues/1433): 添加 `xmake pack` 命令去生成 NSIS/zip/tar.gz/srpm/rpm/runself 安装包 * [#4435](https://github.com/xmake-io/xmake/issues/4435): 为 UnityBuild 的组模式增加 batchsize 支持 * [#4485](https://github.com/xmake-io/xmake/pull/4485): 新增 package.install\_locally 策略支持 * 新增 NetBSD 支持 ### Changes * [#4484](https://github.com/xmake-io/xmake/pull/4484): 改进 swig 规则 * 改进 Haiku 支持 ### Bugs 修复 * [#4372](https://github.com/xmake-io/xmake/issues/4372): 修复 protobuf 规则 * [#4439](https://github.com/xmake-io/xmake/issues/4439): 修复 asn1c 规则 --- --- url: /posts/xmake-update-v2.8.7.md --- ## Introduction of new features In the new version, we have added cosmocc tool chain support. Using it, we can compile once and run everywhere. In addition, we also refactored the implementation of C++ Modules and solved many C++ Modules-related problems. ### Cosmocc toolchain support The cosmocc tool chain is the compilation tool chain provided by the [cosmopolitan](https://github.com/jart/cosmopolitan) project. Programs compiled using this tool chain can be compiled once and run anywhere. In the new version, we also support this tool chain, which can compile programs under macosx/linux/windows, and can also support automatic downloading of the cosmocc tool chain. For users, they only need to configure the xmake.lua project file and then execute the `xmake` command to achieve one-click compilation and run it everywhere. The content of xmake.lua is as follows. This is the most basic construction configuration of the hello world terminal program. ```lua add_rules("mode.debug", "mode.release") add_requires("cosmocc") target("test") set_kind("binary") add_files("src/*.c") set_toolchains("@cosmocc") ``` Then, we execute the xmake command, which will first download the integrated cosmocc tool chain, and then use this tool chain to compile the program. ```bash ruki:console$ xmake checking for platform... linux checking for architecture ...x86_64 note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> cosmocc 3.2.4 please input: y (y/n/m) => install cosmocc 3.2.4 .. ok [25%]: cache compiling.release src/main.c [50%]: linking.release test [100%]: build ok, spent 1.548s ruki:console$ xmake run hello world ``` ### C++ Modules improvements Thank you very much @Arthapz for doing a lot of improvements to C++ Modules, making xmake better support C++ Modules construction, and also fixed many known issues, such as C++ Modules compilation support under msys2/mingw. ### Improve xmake test Starting from version 2.8.5, we have added a built-in test command: `xmake test`. We only need to configure some test cases through add\_tests on the target that needs to be tested to automatically execute the test. The effect of execution is as follows: ```bash ruki-2:test ruki$ xmake test running tests ... [ 2%]: test_1/args .................................... passed 7.000s [ 5%]: test_1/default .................................... passed 5.000s [ 8%]: test_1/fail_output .................................... passed 5.000s [ 11%]: test_1/pass_output .................................... passed 6.000s [ 13%]: test_2/args .................................... passed 7.000s [ 16%]: test_2/default .................................... passed 6.000s [ 19%]: test_2/fail_output .................................... passed 6.000s [ 22%]: test_2/pass_output .................................... passed 6.000s [ 25%]: test_3/args .................................... passed 7.000s ... [ 77%]: test_7/pass_output .................................... failed 5.000s [ 80%]: test_8/args .................................... passed 7.000s [ 83%]: test_8/default .................................... passed 6.000s [ 86%]: test_8/fail_output .................................... passed 6.000s [ 88%]: test_8/pass_output .................................... failed 5.000s [ 91%]: test_9/args .................................... passed 6.000s [ 94%]: test_9/default .................................... passed 6.000s [ 97%]: test_9/fail_output .................................... passed 6.000s [100%]: test_9/pass_output .................................... passed 6.000s 80% tests passed, 7 tests failed out of 36, spent 0.242s ``` In the new version, we have added test support for timeout running. If some test programs run for a long time without exiting, they will get stuck. We can configure the timeout to force exit and return failure. ```lua target("test_timeout") set_kind("binary") set_default(false) add_files("src/run_timeout.cpp") add_tests("run_timeout", {run_timeout = 1000}) ``` In the above configuration, we can configure the timeout period for the specified test program to run through `{run_timeout = 1000}`. If the run times out, the test will fail. ```bash $ xmake test [100%]: test_timeout/run_timeout ............................. failed 1.006s run failed, exit code: -1, exit error: wait process timeout ``` ### Support Android NDK r26b Since Android NDK r26b, NDK has made great changes to the structure of the internal build tool chain and completely uses llvm clang to build programs. Therefore, the new version of xmake has made some adaptations to it so that it can continue to support new N.D.K. ### Improve runtime configuration In addition, we have also improved the `set_runtimes` interface. In addition to the previously supported `MT/MD/MTd/MDd` and other Windows msvc runtime library configurations, we have also added `c++_static`, `c++_shared` `, `stdc++\_static`, `stdc++\_shared\` and other library configurations, They are used in clang/gcc's c++ runtime library configuration. For Android platform compilation, we also merged and unified the existing `xmake f --ndk_cxxstl=` and other configurations into `xmake f --runtimes=`, corresponding to `set_runtimes`. In addition to settings, we can also obtain and determine the current runtimes library in the target through interfaces such as `target:runtimes()` and `target:has_runtime()`. In the package, the same interface is also available. For example: ```lua target("test") add_files("src/*.cpp") on_load(function (target) if target:has_runtime("c++_shared", "c++_static") then -- TODO end end) ``` If the `c++_static` configuration is in effect, flags such as `-stdlib=libc++ -static-libstdc++` will be automatically added when Clang is compiled, and if `stdc++_static` corresponds to `-stdlib=slibtdc++` . ### Improve script matching mode All script configuration interfaces such as `on_xxx`, `before_xxx` and `after_xxx` in xmake can set the platform architecture mode in which the script can be run in the first parameter. The configured script can be executed only if the specified mode matches the current architecture mode. Its complete filtering syntax is as follows: `plat|arch1,arch2@host|arch1,arch2` It looks very complicated, but it is actually very simple. Each stage is optional and can be partially omitted, corresponding to: `Compilation platform|Compilation architecture@Host platform|Host architecture` If you do not set any platform filter conditions, all platforms will be supported by default, and the scripts inside will take effect on all platforms, for example: ```lua on_install(function (package) -- TODO end) ``` If the installation script is effective for a specific platform, then directly specify the corresponding compilation platform. You can specify multiple ones at the same time: ```lua on_install("linux", "macosx", function (package) -- TODO end) ``` If you need to subdivide it into a specific architecture to take effect, you can write like this: ```lua on_install("linux|x86_64", "iphoneos|arm64", function (package) -- TODO end) ``` If you also want to limit the execution host environment platform and architecture, you can append `@host|arch` behind, for example: ```lua on_install("mingw@windows", function (package) -- TODO end) ``` This means that it only takes effect when compiling the mingw platform under windows. We can also not specify which platform and architecture, but only set the host platform and architecture. This is usually used to describe some dependency packages related to compilation tools, which can only be run in the host environment. For example, if the package we compile depends on cmake, we need to add the package description of cmake, thenThe compilation and installation environment inside can only be the host platform: ```lua on_install("@windows", "@linux", "@macosx", function (package) -- TODO end) ``` Some other examples: ```lua -- `@linux` -- `@linux|x86_64` -- `@macosx,linux` -- `android@macosx,linux` -- `android|armeabi-v7a@macosx,linux` -- `android|armeabi-v7a@macosx,linux|x86_64` -- `android|armeabi-v7a@linux|x86_64` ``` In 2.8.7, we improved pattern matching support and added the ability to exclude specified platforms and architectures, such as: ``` !plat|!arch@!subhost|!subarch ``` ```bash @!linux @!linux|x86_64 @!macosx,!linux !android@macosx,!linux android|!armeabi-v7a@macosx,!linux android|armeabi-v7a,!iphoneos@macosx,!linux|x86_64 !android|armeabi-v7a@!linux|!x86_64 !linux|* ``` At the same time, a built-in `native` architecture is also provided to match the local architecture of the current platform, mainly used to specify or exclude cross-compilation platforms. ```lua on_install("macosx|native", ...) ``` The above configuration, if used on a macOS x86\_64 device, will only match the local architecture compilation of `xmake f -a x86_64`. If it is cross-compiled with `xmake f -a arm64`, it will not be matched. In the same way, if you only want to match cross-compilation, you can use `macosx|!native` to negate and exclude. This mode improvement is actually mainly used to simplify the configuration of warehouse packages and better handle the configuration support of package installation scripts on different platforms. ## Changelog ### New features * [#4544](https://github.com/xmake-io/xmake/issues/4544): Support to wait process timeout for `xmake test` * [#4606](https://github.com/xmake-io/xmake/pull/4606): Add `add_versionfiles` api in package * [#4709](https://github.com/xmake-io/xmake/issues/4709): Add cosmocc toolchain support * [#4715](https://github.com/xmake-io/xmake/issues/4715): Add is\_cross() api in description scope * [#4747](https://github.com/xmake-io/xmake/issues/4747): Add `build.always_update_configfiles` policy ### Changes * [#4575](https://github.com/xmake-io/xmake/issues/4575): Check invalid scope name * Add more loong64 support * Improve dlang/dmd support for frameworks * [#4571](https://github.com/xmake-io/xmake/issues/4571): Improve `xmake test` output * [#4609](https://github.com/xmake-io/xmake/issues/4609): Improve to detect vs build tool envirnoments * [#4614](https://github.com/xmake-io/xmake/issues/4614): Support android ndk 26b * [#4473](https://github.com/xmake-io/xmake/issues/4473): Enable warning output by default * [#4477](https://github.com/xmake-io/xmake/issues/4477): Improve runtimes to support libc++/libstdc++ * [#4657](https://github.com/xmake-io/xmake/issues/4657): Improve to select script pattern * [#4673](https://github.com/xmake-io/xmake/pull/4673): Refactor modules support * [#4746](https://github.com/xmake-io/xmake/pull/4746): Add native modules support for cmake generator ### Bugs Fixed * [#4596](https://github.com/xmake-io/xmake/issues/4596): Fix remote build cache * [#4689](https://github.com/xmake-io/xmake/issues/4689): Fix deps inherit --- --- url: /zh/posts/xmake-update-v2.8.7.md --- ## 新特性介绍 新版本中,我们新增了 cosmocc 工具链支持,使用它,我们可以实现一次编译,到处运行。另外,我们还重构了 C++ Modules 的实现,解决了很多 C++ Modules 相关的问题。 ### Cosmocc 工具链支持 cosmocc 工具链是 [cosmopolitan](https://github.com/jart/cosmopolitan) 项目提供的编译工具链,使用这个工具链编译的程序可以实现一次编译,到处运行。 而新版本中,我们对这个工具链也做了支持,可以实现在 macosx/linux/windows 下编译程序,并且还能够支持自动下载 cosmocc 工具链。 对于用户,仅仅只需要配置 xmake.lua 工程文件,然后执行 `xmake` 命令,即可实现一键编译,到处运行。 xmake.lua 内容如下,这是一个最基础的 hello world 终端程序的构建配置。 ```lua add_rules("mode.debug", "mode.release") add_requires("cosmocc") target("test") set_kind("binary") add_files("src/*.c") set_toolchains("@cosmocc") ``` 然后,我们执行 xmake 命令,它会先下载集成 cosmocc 工具链,然后使用这个工具链去编译程序。 ```bash ruki:console$ xmake checking for platform ... linux checking for architecture ... x86_64 note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> cosmocc 3.2.4 please input: y (y/n/m) => install cosmocc 3.2.4 .. ok [ 25%]: cache compiling.release src/main.c [ 50%]: linking.release test [100%]: build ok, spent 1.548s ruki:console$ xmake run hello world ``` ### C++ Modules 改进 非常感谢 @Arthapz 对 C++ Modules 做了大量的改进工作,使得 xmake 更好地支持 C++ Modules 构建,也修复了很多已知的问题,例如 msys2/mingw 下对 C++ Modules 编译支持等问题。 ### 改进 xmake test 2.8.5 版本开始,我们增加了内置的测试命令:`xmake test`,我们只需要在需要测试的 target 上通过 add\_tests 配置一些测试用例,就可以自动执行测试。 执行的效果如下: ```bash ruki-2:test ruki$ xmake test running tests ... [ 2%]: test_1/args .................................... passed 7.000s [ 5%]: test_1/default .................................... passed 5.000s [ 8%]: test_1/fail_output .................................... passed 5.000s [ 11%]: test_1/pass_output .................................... passed 6.000s [ 13%]: test_2/args .................................... passed 7.000s [ 16%]: test_2/default .................................... passed 6.000s [ 19%]: test_2/fail_output .................................... passed 6.000s [ 22%]: test_2/pass_output .................................... passed 6.000s [ 25%]: test_3/args .................................... passed 7.000s ... [ 77%]: test_7/pass_output .................................... failed 5.000s [ 80%]: test_8/args .................................... passed 7.000s [ 83%]: test_8/default .................................... passed 6.000s [ 86%]: test_8/fail_output .................................... passed 6.000s [ 88%]: test_8/pass_output .................................... failed 5.000s [ 91%]: test_9/args .................................... passed 6.000s [ 94%]: test_9/default .................................... passed 6.000s [ 97%]: test_9/fail_output .................................... passed 6.000s [100%]: test_9/pass_output .................................... passed 6.000s 80% tests passed, 7 tests failed out of 36, spent 0.242s ``` 而新版本中,我们新增了对超时运行的测试支持。 如果一些测试程序长时间运行不退出,就会卡住,我们可以通过配置超时时间,强制退出,并返回失败。 ```lua target("test_timeout") set_kind("binary") set_default(false) add_files("src/run_timeout.cpp") add_tests("run_timeout", {run_timeout = 1000}) ``` 上面的配置中,我们通过 `{run_timeout = 1000}` 可以配置指定的测试程序运行的超时时间,如果运行超时,就会作为测试失败。 ```bash $ xmake test [100%]: test_timeout/run_timeout .................................... failed 1.006s run failed, exit code: -1, exit error: wait process timeout ``` ### 支持 Android NDK r26b 自从 Android NDK r26b 之后,NDK 对内部构建工具链的结构做了很大的改动,完全采用 llvm clang 来构建程序,因此新版本 xmake 对它做了一些适配,使得能够继续很好地支持新的 NDK。 ### 改进运行时配置 另外,我们还改进了 `set_runtimes` 接口,除了先前已经支持的 `MT/MD/MTd/MDd` 等 windows msvc 的运行时库配置,还新增了 `c++_static`, `c++_shared`, `stdc++_static`, `stdc++_shared` 等库配置, 它们用于 clang/gcc 的 c++ 运行时库配置。而对于 Android 平台编译, 我们也将已有的 `xmake f --ndk_cxxstl=` 等配置,也合并统一到 `xmake f --runtimes=` 中,与 `set_runtimes` 相对应。 除了设置,我们也可以在 target 中,通过 `target:runtimes()` 和 `target:has_runtime()` 等接口去获取和判断当前的 runtimes 库,在 package 中,也有一样的接口可用。 例如: ```lua target("test") add_files("src/*.cpp") on_load(function (target) if target:has_runtime("c++_shared", "c++_static") then -- TODO end end) ``` 如果 `c++_static` 配置生效,在 Clang 编译的时候,就会被自动添加 `-stdlib=libc++ -static-libstdc++` 等 flags,而如果 `stdc++_static` 则对应 `-stdlib=slibtdc++`。 ### 改进脚本匹配模式 xmake 中的所有 `on_xxx`, `before_xxx` 和 `after_xxx` 等脚本配置接口,都可以在第一个参数中,设置脚本能够被运行的平台架构模式。 如果指定的模式和当前架构模式匹配,配置的脚本才能够被执行,它的完整的过滤语法如下:`plat|arch1,arch2@host|arch1,arch2` 看上去非常的复杂,其实很简单,其中每个阶段都是可选的,可部分省略,对应:`编译平台|编译架构@主机平台|主机架构` 如果不设置任何平台过滤条件,那么默认全平台支持,里面的脚本对所有平台生效,例如: ```lua on_install(function (package) -- TODO end) ``` 如果安装脚本对特定平台生效,那么直接指定对应的编译平台,可以同时指定多个: ```lua on_install("linux", "macosx", function (package) -- TODO end) ``` 如果还要细分到指定架构才能生效,可以这么写: ```lua on_install("linux|x86_64", "iphoneos|arm64", function (package) -- TODO end) ``` 如果还要限制执行的主机环境平台和架构,可以在后面追加`@host|arch`,例如: ```lua on_install("mingw@windows", function (package) -- TODO end) ``` 意思就是仅对windows下编译mingw平台生效。 我们也可以不指定比那一平台和架构,仅设置主机平台和架构,这通常用于描述一些跟编译工具相关的依赖包,只能在主机环境运行。 例如,我们编译的包,依赖了cmake,需要添加cmake的包描述,那么里面编译安装环境,只能是主机平台: ```lua on_install("@windows", "@linux", "@macosx", function (package) -- TODO end) ``` 其他一些例子: ```lua -- `@linux` -- `@linux|x86_64` -- `@macosx,linux` -- `android@macosx,linux` -- `android|armeabi-v7a@macosx,linux` -- `android|armeabi-v7a@macosx,linux|x86_64` -- `android|armeabi-v7a@linux|x86_64` ``` 而在 2.8.7 中,我们改进了模式匹配支持,新增排除指定平台和架构,例如: ``` !plat|!arch@!subhost|!subarch ``` ```bash @!linux @!linux|x86_64 @!macosx,!linux !android@macosx,!linux android|!armeabi-v7a@macosx,!linux android|armeabi-v7a,!iphoneos@macosx,!linux|x86_64 !android|armeabi-v7a@!linux|!x86_64 !linux|* ``` 同时,还提供了一个内置的 `native` 架构,用于匹配当前平台的本地架构,主要用于指定或者排除交叉编译平台。 ```lua on_install("macosx|native", ...) ``` 上面的配置,如果在 macOS x86\_64 的设备上,它仅仅只会匹配 `xmake f -a x86_64` 的本地架构编译。 如果是 `xmake f -a arm64` 交叉编译,就不会被匹配到。 同理,如果只想匹配交叉编译,可以使用 `macosx|!native` 进行取反排除就行了。 这个模式改进,其实主要用于仓库包配置的简化,更好的处理不同平台下包安装脚本的配置支持。 ## 更新日志 ### 新特性 * [#4544](https://github.com/xmake-io/xmake/issues/4544): 改进 `xmake test`,支持等待进程超时 * [#4606](https://github.com/xmake-io/xmake/pull/4606): 为 package 添加 `add_versionfiles` 接口 * [#4709](https://github.com/xmake-io/xmake/issues/4709): 添加 cosmocc 工具链支持 * [#4715](https://github.com/xmake-io/xmake/issues/4715): 在描述域添加 is\_cross() 接口 * [#4747](https://github.com/xmake-io/xmake/issues/4747): 添加 `build.always_update_configfiles` 策略 ### 改进 * [#4575](https://github.com/xmake-io/xmake/issues/4575): 检测无效的域参数 * 添加更多的 loong64 支持 * 改进 dlang/dmd 对 frameworks 的支持 * [#4571](https://github.com/xmake-io/xmake/issues/4571): 改进 `xmake test` 的输出支持 * [#4609](https://github.com/xmake-io/xmake/issues/4609): 改进探测 vs 构建工具环境 * [#4614](https://github.com/xmake-io/xmake/issues/4614): 改进支持 android ndk 26b * [#4473](https://github.com/xmake-io/xmake/issues/4473): 默认启用警告输出 * [#4477](https://github.com/xmake-io/xmake/issues/4477): 改进 runtimes 去支持 libc++/libstdc++ * [#4657](https://github.com/xmake-io/xmake/issues/4657): 改进脚本的模式匹配 * [#4673](https://github.com/xmake-io/xmake/pull/4673): 重构模块支持 * [#4746](https://github.com/xmake-io/xmake/pull/4746): 为 cmake generator 添加原生 c++ modules 支持 ### Bugs 修复 * [#4596](https://github.com/xmake-io/xmake/issues/4596): 修复远程构建缓存 * [#4689](https://github.com/xmake-io/xmake/issues/4689): 修复目标依赖继承 --- --- url: /posts/xmake-update-v2.9.1.md --- ## Introduction of new features In the new version, we have added native tool chain support for Hongmeng system and implemented a new native Lua module import support. In addition, we have also made a lot of optimizations to the build speed, and the effect is very obvious. ### Add Hongmeng SDK tool chain support We have added native toolchain compilation support for the Hongmeng OS platform: ```bash $ xmake f -p harmony ``` xmake will also automatically detect the default SDK path. Of course, we can also specify the Harmony SDK path. ```bash $ xmake f -p Harmony --sdk=/Users/ruki/Library/Huawei/Sdk/... ``` ### Add native module support We know that in xmake, you can import some lua modules through the import interface for use in the script domain. However, if the operation of some modules is time-consuming, then lua implementation is not an ideal choice. Therefore, in the new version, we have added support for the native lua module, which can be implemented through native to achieve speed-up optimization. Moreover, importing and using the module is as simple as the lua module. When using native modules, xmake will perform two stages of compilation. First, it will automatically compile the native module, and then import the module into lua as a library or binary. For users, they only need to call import to import. #### Define dynamic library module The advantage of the dynamic library module is that it not only achieves performance acceleration through native, but also avoids the creation of additional sub-processes for each call, making it more lightweight and further improving the speed. We can first define a dynamic library module, which fully supports all C APIs of Lua, so we can also directly introduce some third-party open source Lua native modules for use. Here we also have a complete example of importing the lua-cjson module for reference: [native\_module\_cjson](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/native_module_cjson) First, we first implement the shared native code, so the interface is exported through the lua API. ./modules/foo/foo.c ```c++ #include static int c_add(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a + b); return 1; } static int c_sub(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a - b); return 1; } int luaopen(foo, lua_State* lua) { //Collect add and sub static const luaL_Reg funcs[] = { {"add", c_add}, {"sub", c_sub}, {NULL, NULL} }; lua_newtable(lua); // pass function list luaL_setfuncs(lua, funcs, 0); return 1; } ``` Notice here that we have included an interface header file of `xmi.h`. In fact, we can also directly introduce `lua.h` and `luaconf.h`. The effect is the same, but it will provide better cross-platform performance. , it will automatically handle the differences between lua/luajit and versions internally. Then, we configure `add_rules("modules.shared")` to compile as a shared native module without introducing any other dependencies. Even Lua dependencies do not need to be introduced, because the xmake main program has exported all Lua interfaces and can be used directly, so the entire module is very lightweight. ./modules/foo/xmake.lua ```lua add_rules("mode.debug", "mode.release") target("foo") -- Specify the target as the library lua module add_rules("module.shared") add_files("foo.c") ``` #### Define binary module In addition to the dynamic library module, we also provide the import of another binary module. It is actually an executable file. Every time the module interface is called, a child process will be called. So what are the benefits of it? Although it is not as efficient as the dynamic library module, its module implementation is simpler. There is no need to call the lua API. It only needs to process the parameter data and output the return value through stdout. In addition, compared to binary distribution, it is distributed through source code, so it also solves the cross-platform problem. Whether to use a dynamic library module or a binary module depends on your needs. If you want a simple implementation, you can consider a binary module. If you want to be efficient, use a dynamic library module. In addition, if you need to speed up through parallel execution, you can also use binary modules. ./modules/bar/bar.cpp ```c++ #include #include #include int main(int argc, char** argv) { int a = atoi(argv[1]); int b = atoi(argv[2]); printf("%d", a + b); return 0; } ``` ./modules/bar/xmake.lua ```lua add_rules("mode.debug", "mode.release") target("add") -- Specify the target as a binary lua module add_rules("module.binary") add_files("bar.cpp") ``` #### Import native module For module import, we only need to call import, which is exactly the same as importing lua modules. ./xmake.lua ```lua add_rules("mode.debug", "mode.release") --Add native modules in the ./modules directory add_moduledirs("modules") target("test") set_kind("phony") on_load(function(target) import("foo", {always_build = true}) import("bar") print("foo: 1 + 1 = %s", foo.add(1, 1)) print("foo: 1 - 1 = %s", foo.sub(1, 1)) print("bar: 1 + 1 = %s", bar.add(1, 1)) end) ``` Since the construction of the plug-in module is completely independent from the main project, the native module will only be built once. If you want to trigger incremental plug-in compilation, you need to configure `always_build = true`, so that xmake will detect it every time Check whether the plug-in code has been changed. If so, the plug-in will be automatically incrementally built. The first execution effect is as follows: ```bash ruki-2:native_module ruki$ xmake [50%]: cache compiling.release src/foo.c [50%]: cache compiling.release src/bar.c [75%]: linking.release libmodule_foo.dylib [75%]: linking.release module_bar [100%]: build ok, spent 1.296s foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` When executed for the second time, the plug-in will not be built and the module can be used directly: ```bash ruki-2:native_module ruki$ xmake foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` #### Use as codegen Through the new native module feature, we can also use it to implement auto-codegen, and then continue to execute the subsequent compilation process based on the automatically generated code. There is also a complete example here for reference: [autogen\_shared\_module](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/autogen_shared_module). ### Add signal module In the new version, we have also added a new signal registration interface. We can register signal processing functions such as SIGINT at the Lua layer to customize the response logic. #### signal.register This interface is used to register signal processors. Currently, it only supports the processing of SIGINT signals. It also supports mainstream platforms such as windows. ```lua import("core.base.signal") function main() signal.register(signal.SIGINT, function (signo) print("signal.SIGINT(%d)", signo) end) io.read() end ``` This is useful when some sub-processes internally shield SIGINT, causing them to freeze and not exit. Even if the user presses `Ctrl+C` to exit the xmake process, it does not exit. We can force it out in this way. ```lua import("core.base.process") import("core.base.signal") function main() local proc signal.register(signal.SIGINT, function (signo) print("sigint") if proc then proc:kill() end end) proc = process.open("./trap.sh") if proc then proc:wait() proc:close() end end ``` For the background of this issue, please refer to: [#4889](https://github.com/xmake-io/xmake/issues/4889) #### signal.ignore We can also ignore the processing of blocking a certain signal through the `signal.ignore` interface. ```lua signal.ignore(signal.SIGINT) ``` #### signal.reset We can also clear the processing function of a certain signal and fall back to the default processing logic. ```lua signal.reset(signal.SIGINT) ``` ### Add support for cppfront/h2 We've also improved support for the latest version of cppfront. The new version of cppfront added new .h2 header files, so we also added support for it. Thanks for the contribution from @shaoxie1986 ```lua add_rules("mode.debug", "mode.release") add_requires("cppfront") target("test") add_rules("cppfront") set_kind("binary") add_files("src/*.cpp2") add_files("src/*.h2") add_packages("cppfront") ``` ### Improve build speed In the new version, we have also fixed an issue related to parallel builds. After restructuring the scheduler, the build speed has been significantly improved. Especially in incremental compilation scenarios where cpp file compilation takes very slow time, the effect is more obvious. For relevant background, see: [#4928](https://github.com/xmake-io/xmake/issues/4928). ## Changelog ### New features * [#4874](https://github.com/xmake-io/xmake/pull/4874): Add Harmony SDK support * [#4889](https://github.com/xmake-io/xmake/issues/4889): Add signal module to register signal handler in lua * [#4925](https://github.com/xmake-io/xmake/issues/4925): Add native modules support * [#4938](https://github.com/xmake-io/xmake/issues/4938): Support for cppfront/h2 ### Changes * Improve packages to support for clang-cl * [#4893](https://github.com/xmake-io/xmake/issues/4893): Improve rc includes deps * [#4928](https://github.com/xmake-io/xmake/issues/4928): Improve to build and link speed * [#4931](https://github.com/xmake-io/xmake/pull/4931): Update pdcurses * [#4973](https://github.com/xmake-io/xmake/issues/4973): Improve to select script ### Bugs fixed * [#4882](https://github.com/xmake-io/xmake/issues/4882): Fix install deps with --group * [#4877](https://github.com/xmake-io/xmake/issues/4877): Fix compile error for xpack with unity build * [#4887](https://github.com/xmake-io/xmake/issues/4887): Fix object deps --- --- url: /zh/posts/xmake-update-v2.9.1.md --- ## 新特性介绍 新版本中,我们新增了鸿蒙系统的 native 工具链支持,并且实现了一种新的 native 原生 lua 模块的导入支持。另外,我们也对构建速度做了很多的优化,效果非常明显。 ### 添加鸿蒙 SDK 工具链支持 我们新增了鸿蒙 OS 平台的 native 工具链编译支持: ```bash $ xmake f -p harmony ``` xmake 也会自动探测默认的 SDK 路径,当然我们也可以指定 Harmony SDK 路径。 ```bash $ xmake f -p Harmony --sdk=/Users/ruki/Library/Huawei/Sdk/... ``` ### 添加 native 模块支持 我们知道,在 xmake 中,可以通过 import 接口去导入一些 lua 模块在脚本域中使用,但是如果一些模块的操作比较耗时,那么 lua 实现并不是理想的选择。 因此,新版本中,我们新增了 native lua 模块的支持,可以通过 native 实现,来达到提速优化的效果,并且模块导入和使用,还是跟 lua 模块一样简单。 使用原生模块时,xmake 会进行两段编译,先会自动编译原生模块,后将模块导入 lua 作为库或二进制,而对于用户,仅仅只需要调用 import 导入即可。 #### 定义动态库模块 动态库模块的好处是,不仅仅通过 native 实现了性能加速,另外避免了每次调用额外的子进程创建,因此更加的轻量,速度进一步得到提升。 我们可以先定义一个动态库模块,里面完全支持 lua 的所有 c API,因此我们也可以将一些第三方的开源 lua native 模块直接引入进来使用。 这里我们也有一个完整的导入 lua-cjson 模块的例子可以参考:[native\_module\_cjson](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/native_module_cjson) 首先,我们先实现 shared 的 native 代码,所以接口通过 lua API 导出。 ./modules/foo/foo.c ```c++ #include static int c_add(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a + b); return 1; } static int c_sub(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a - b); return 1; } int luaopen(foo, lua_State* lua) { // 收集add和sub static const luaL_Reg funcs[] = { {"add", c_add}, {"sub", c_sub}, {NULL, NULL} }; lua_newtable(lua); // 传递函数列表 luaL_setfuncs(lua, funcs, 0); return 1; } ``` 注意到这里,我们 include 了一个 `xmi.h` 的接口头文件,其实我们也可以直接引入 `lua.h`,`luaconf.h`,效果是一样的,但是会提供更好的跨平台性,内部会自动处理 lua/luajit还有版本间的差异。 然后,我们配置 `add_rules("modules.shared")` 作为 shared native 模块来编译,不需要引入任何其他依赖。 甚至连 lua 的依赖也不需要引入,因为 xmake 主程序已经对其导出了所有的 lua 接口,可直接使用,所以整个模块是非常轻量的。 ./modules/foo/xmake.lua ```lua add_rules("mode.debug", "mode.release") target("foo") -- 指定目标为库lua模块 add_rules("module.shared") add_files("foo.c") ``` #### 定义二进制模块 出了动态库模块,我们还提供了另外一种二进制模块的导入。它其实就是一个可执行文件,每次调用模块接口,都会去调用一次子进程。 那它有什么好处呢,尽管它没有动态库模块那么高效,但是它的模块实现更加的简单,不需要调用 lua API,仅仅只需要处理参数数据,通过 stdout 去输出返回值即可。 另外,相比二进制分发,它是通过源码分发的,因此也解决了跨平台的问题。 具体是使用动态库模块,还是二进制模块,具体看自己的需求,如果想要实现简单,可以考虑二进制模块,如果想要高效,就用动态库模块。 另外,如果需要通过并行执行来提速,也可以使用二进制模块。 ./modules/bar/bar.cpp ```c++ #include #include #include int main(int argc, char** argv) { int a = atoi(argv[1]); int b = atoi(argv[2]); printf("%d", a + b); return 0; } ``` ./modules/bar/xmake.lua ```lua add_rules("mode.debug", "mode.release") target("add") -- 指定目标为二进制lua模块 add_rules("module.binary") add_files("bar.cpp") ``` #### 导入原生模块 对于模块导入,我们仅仅需要调用 import,跟导入 lua 模块的用法完全一致。 ./xmake.lua ```lua add_rules("mode.debug", "mode.release") -- 添加./modules目录内原生模块 add_moduledirs("modules") target("test") set_kind("phony") on_load(function(target) import("foo", {always_build = true}) import("bar") print("foo: 1 + 1 = %s", foo.add(1, 1)) print("foo: 1 - 1 = %s", foo.sub(1, 1)) print("bar: 1 + 1 = %s", bar.add(1, 1)) end) ``` 由于插件模块的构建是跟主工程完全独立的,因此,native 模块只会被构建一次,如果想要触发增量的插件编译,需要配置上 `always_build = true`,这样,xmake 就会每次检测插件代码是否有改动,如果有改动,会自动增量构建插件。 首次执行效果如下: ```bash ruki-2:native_module ruki$ xmake [ 50%]: cache compiling.release src/foo.c [ 50%]: cache compiling.release src/bar.c [ 75%]: linking.release libmodule_foo.dylib [ 75%]: linking.release module_bar [100%]: build ok, spent 1.296s foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` 第二次执行,就不会再构建插件,可以直接使用模块: ```bash ruki-2:native_module ruki$ xmake foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` #### 作为 codegen 来使用 通过新的 native 模块特性,我们也可以用来实现 auto-codegen,然后根据自动生成的代码,继续执行后续编译流程。 这里也有完整的例子可以参考:[autogen\_shared\_module](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/autogen_shared_module)。 ### 添加 signal 模块 新版本中,我们还新增了信号注册接口,我们可以在 lua 层,注册 SIGINT 等信号处理函数,来定制化响应逻辑。 #### signal.register 这个接口用于注册信号处理器,目前仅仅支持 SIGINT 信号的处理,同时它也是支持 windows 等主流平台的。 ```lua import("core.base.signal") function main() signal.register(signal.SIGINT, function (signo) print("signal.SIGINT(%d)", signo) end) io.read() end ``` 这对于当一些子进程内部屏蔽了 SIGINT,导致卡死不退出,即使用户按了 `Ctrl+C` 退出了 xmake 进程,它也没有退出时候, 我们就可以通过这种方式去强制退掉它。 ```lua import("core.base.process") import("core.base.signal") function main() local proc signal.register(signal.SIGINT, function (signo) print("sigint") if proc then proc:kill() end end) proc = process.open("./trap.sh") if proc then proc:wait() proc:close() end end ``` 关于这个问题的背景,可以参考:[#4889](https://github.com/xmake-io/xmake/issues/4889) #### signal.ignore 我们也可以通过 `signal.ignore` 这个接口,去忽略屏蔽某个信号的处理。 ```lua signal.ignore(signal.SIGINT) ``` #### signal.reset 我们也可以清除某个信号的处理函数,回退到默认的处理逻辑。 ```lua signal.reset(signal.SIGINT) ``` ### 增加对 cppfront/h2 的支持 我们还改进了对 cppfront 的最新版本支持,而新版本的 cppfront 增加了 .h2 头文件的处理,因此我们也增加了对它的支持。 感谢来自 @shaoxie1986 的贡献。、 ```lua add_rules("mode.debug", "mode.release") add_requires("cppfront") target("test") add_rules("cppfront") set_kind("binary") add_files("src/*.cpp2") add_files("src/*.h2") add_packages("cppfront") ``` ### 改进构建速度 新版本中,我们还修复一个并行构建相关的问题,经过对调度器的重构,构建速度得到明显的提升,尤其在 cpp 文件编译耗时非常慢的增量编译场景,效果更为明显。 相关背景见:[#4928](https://github.com/xmake-io/xmake/issues/4928) ## 更新日志 ### 新特性 * [#4874](https://github.com/xmake-io/xmake/pull/4874): 添加鸿蒙 SDK 支持 * [#4889](https://github.com/xmake-io/xmake/issues/4889): 添加 signal 模块 去注册信号处理 * [#4925](https://github.com/xmake-io/xmake/issues/4925): 添加 native 模块支持 * [#4938](https://github.com/xmake-io/xmake/issues/4938): 增加对 cppfront/h2 的支持 ### 改进 * 改进包管理,支持切换 clang-cl * [#4893](https://github.com/xmake-io/xmake/issues/4893): 改进 rc 头文件依赖检测 * [#4928](https://github.com/xmake-io/xmake/issues/4928): 改进构建和链接速度,增量编译时候效果更加明显 * [#4931](https://github.com/xmake-io/xmake/pull/4931): 更新 pdcurses * [#4973](https://github.com/xmake-io/xmake/issues/4973): 改进选择脚本的匹配模式 ### Bugs 修复 * [#4882](https://github.com/xmake-io/xmake/issues/4882): 修复安装组依赖问题 * [#4877](https://github.com/xmake-io/xmake/issues/4877): 修复 xpack 打包时,unit build 编译失败问题 * [#4887](https://github.com/xmake-io/xmake/issues/4887): 修复 object 依赖链接 --- --- url: /posts/xmake-update-v3.0.5.md --- ## Introduction of new features In the new version, we have introduced several major features that significantly enhance the development experience. The highlights include **multi-row progress output** with theme support for better build visibility, a comprehensive **XML module** for parsing and encoding XML data, **asynchronous OS APIs** for improved I/O performance, and **Swift interop support** for seamless integration between Swift and C++/Objective-C code. We have also made significant improvements to toolchain configuration, TTY handling, and various performance optimizations. ### Support multi-row refresh for progress output We have improved the progress output to support multi-row refresh, providing a significantly better visual experience during long-running builds. Instead of updating a single progress line, the build output now displays multiple concurrent build tasks with their individual progress, making it easier to monitor parallel compilation. The output now shows multiple progress lines for parallel builds with real-time status updates for each compilation task: ![progress](/assets/img/progress-multirow.png) You can enable multi-row progress output in two ways: 1. **Via theme configuration**: Use the `soong` theme which includes multi-row progress by default: ```bash $ xmake g --theme=soong ``` 2. **Via project policy**: Enable it directly in your `xmake.lua`: ```lua set_policy("build.progress_style", "multirow") ``` This provides better visibility into parallel build progress, makes it easier to identify slow compilation units, and improves the overall user experience for large projects with many source files or parallel builds with multiple compilation units. For more details, see: [#6974](https://github.com/xmake-io/xmake/pull/6974) ### Add Swift interop support for C++ and Objective-C We have added comprehensive Swift interop support, enabling seamless bidirectional interoperability between Swift and C++/Objective-C code in your projects. The `swift.interop` rule is automatically enabled when the `swift.interop` target value is set, making it easy to mix Swift and C++ code in the same project. The Swift interop support includes: * Bidirectional Swift-C++ interoperability * Automatic header generation for C++ to call Swift functions * Support for both Objective-C and C++ interop modes * Swift static library archiver toolset for enhanced compilation workflows **Target values:** You can configure Swift interop using the following target values: ```lua set_values("swift.modulename", "SwiftFibonacci") -- Set the Swift module name set_values("swift.interop", "cxx") -- Enable interop: "objc" or "cxx" set_values("swift.interop.headername", "fibonacci-Swift.h") -- Define output header name set_values("swift.interop.cxxmain", true) -- Force -parse-as-library to avoid duplicate main symbols ``` **Complete example: Swift-C++ interop** Here's a complete example demonstrating Swift-C++ bidirectional interoperation: **fibonacci.swift:** ```swift // fibonacci.swift public func fibonacciSwift(_ x: CInt) -> CInt { print("x [swift]: \(x)") if x <= 1 { return 1 } return fibonacciSwift(x - 1) + fibonacciSwift(x - 2) } ``` **main.cpp:** ```cpp // main.cpp #include #include int main(int argc, char ** argv) { std::cout << SwiftFibonacci::fibonacciSwift(5) << std::endl; return 0; } ``` **xmake.lua:** ```lua -- xmake.lua target("cxx_interop") set_kind("binary") set_languages("cxx20") add_files("lib/**.swift", {public = true}) add_files("src/**.cpp") set_values("swift.modulename", "SwiftFibonacci") set_values("swift.interop", "cxx") set_values("swift.interop.headername", "fibonacci-Swift.h") set_values("swift.interop.cxxmain", true) ``` **Build output:** ```bash $ xmake checking for platform ... macosx checking for architecture ... x86_64 checking for Xcode directory ... /Applications/Xcode.app [ 3%]: generating.swift.header fibonacci-Swift.h [ 38%]: cache compiling.release src/fibonacci.cpp [ 56%]: compiling.release lib/fibonacci/fibonacci.swift [ 76%]: linking.release cxx_interop [100%]: build ok, spent 1.785s $ xmake run x [swift]: 5 x [swift]: 4 ... 8 ``` When `swift.interop` is set, xmake automatically generates the C++ header file that allows C++ code to call Swift functions. You can use `swift.modulename` to define the Swift module name, which becomes the namespace in C++. Choose between `"objc"` for Objective-C interop or `"cxx"` for C++ interop. When both Swift and C++ have main functions, use `swift.interop.cxxmain` to avoid duplicate main symbols. This feature is particularly useful for: * Migrating existing C++ projects to Swift incrementally * Using high-performance C++ libraries in Swift applications * Creating Swift wrappers for C++ APIs * Building cross-platform applications that leverage both languages * Calling Swift code from C++ applications seamlessly For more details, see: [#6967](https://github.com/xmake-io/xmake/pull/6967) ### Add XML module with parsing and encoding support We have introduced a new `core.base.xml` module that provides a tiny DOM-style XML toolkit working inside Xmake's sandbox. It focuses on predictable data structures, JSON-like usability, and optional streaming so you can parse large XML documents without building the entire tree. The XML module features: * DOM-style node structure using plain Lua tables * Streaming parser for large files (`xml.scan`) * XPath-like queries (`xml.find`) * Convenient file I/O helpers (`xml.loadfile`, `xml.savefile`) * Support for comments, CDATA, DOCTYPE, and unquoted attributes * Pretty printing with customizable indentation **Node Structure:** XML nodes are plain Lua tables with the following structure: ```lua { name = "element-name" | nil, -- only for element nodes kind = "element" | "text" | "comment" | "cdata" | "doctype" | "document", attrs = { key = value, ... } or nil, text = string or nil, children = { child1, child2, ... } or nil, prolog = { comment/doctype nodes before root } or nil } ``` **Basic usage:** ```lua import("core.base.xml") -- Parse XML string local doc = assert(xml.decode([[ hello ]])) -- Find and modify nodes local item = assert(xml.find(doc, "//item[@id='foo']")) item.attrs.lang = "en" -- mutate attrs directly item.children = {xml.text("world")} -- replace existing text node -- Add comment table.insert(doc.children, xml.comment("generated by xmake")) -- Encode with pretty printing local pretty = assert(xml.encode(doc, {pretty = true})) assert(xml.savefile("out.xml", doc, {pretty = true})) ``` **File operations:** ```lua import("core.base.xml") -- Load from file local plist = assert(xml.loadfile("Info.plist")) -- Modify and save local dict = assert(xml.find(plist, "plist/dict")) -- ... modify nodes ... assert(xml.savefile("Info.plist", plist, {pretty = true, indent = 2})) ``` **Streaming parser for large files:** ```lua import("core.base.xml") local found xml.scan(plist_text, function(node) if node.name == "key" and xml.text_of(node) == "NSPrincipalClass" then found = node return false -- early terminate end end) ``` `xml.scan` walks nodes as they are completed; returning `false` stops the scan immediately. This is ideal for large files when you only need a few entries. **XPath-like queries:** ```lua import("core.base.xml") local doc = assert(xml.loadfile("config.xml")) -- Find by path local element = xml.find(doc, "/root/item") -- Find by attribute local item = xml.find(doc, "//item[@id='foo']") -- Find by text content local node = xml.find(doc, "//string[text()='value']") -- Get text content local text = xml.text_of(node) ``` **Node creation helpers:** ```lua import("core.base.xml") local textnode = xml.text("hello") local empty = xml.empty("br", {class = "line"}) local comment = xml.comment("generated by xmake") local cdata_node = xml.cdata("if (value < 1) {...}") local doctype = xml.doctype('plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"') ``` **Options:** | Option | Applies to | Description | |--------|------------|-------------| | `trim_text = true` | `xml.decode`, `xml.scan` | Strip leading/trailing spaces inside text nodes | | `keep_whitespace_nodes = true` | `xml.decode`, `xml.scan` | Preserve whitespace-only text nodes | | `pretty = true` / `indent` / `indentchar` | `xml.encode`, `xml.savefile` | Enable formatting and control indentation | **Use cases:** * Reading and modifying IDE project configurations (Info.plist, .vcxproj, etc.) * Generating XML-based project files * Processing build metadata and reports * Parsing large XML files efficiently with streaming * Converting between XML and Lua data structures For more details, see: [#7025](https://github.com/xmake-io/xmake/pull/7025) ### Add JSON output format for target information We have added JSON output format support for the `xmake show` command, making it easier to programmatically extract target information. This feature enables seamless integration with IDEs, build automation tools, and custom scripts that need to parse xmake project metadata. The JSON output includes comprehensive target information: * Target name, kind, and file paths * Source files and header files * Compiler flags and defines * Link libraries and linker flags * Include directories and dependencies * Configuration options and rules You can use `--json` for compact output or `--pretty-json` for formatted output: ```bash $ xmake show -t target --json {"targets":[{"name":"test","kind":"binary","files":["src/main.cpp"],"links":["pthread"],"defines":["DEBUG"]}]} $ xmake show -t target --pretty-json { "targets": [ { "name": "test", "kind": "binary", "files": ["src/main.cpp"], "links": ["pthread"], "defines": ["DEBUG"], "includedirs": ["include"], "cxxflags": ["-std=c++17"], "deps": ["mylib"] } ] } ``` You can extract target information for IDE integration or use it in scripts: ```bash # Extract target information for IDE integration xmake show -t target --pretty-json > project_info.json # Use in scripts TARGET_INFO=$(xmake show -t target --json) TARGET_NAME=$(echo $TARGET_INFO | jq -r '.targets[0].name') ``` This is particularly useful for: * IDE integration (VS Code, CLion, etc.) * Automated build systems and CI/CD pipelines * Custom project analysis tools * Documentation generation For more details, see: [#7024](https://github.com/xmake-io/xmake/pull/7024) ### Support specifying CUDA SDK version We have added support for specifying the CUDA SDK version via the `cuda_sdkver` configuration option, giving you precise control over CUDA compilation. This is essential when working with multiple CUDA installations or when you need to target a specific CUDA version for compatibility. You can specify the CUDA SDK version for a target: ```lua target("cuda_app") set_kind("binary") add_files("src/*.cu") add_rules("cuda") set_values("cuda.sdkver", "12.0") -- Specify CUDA SDK version ``` You can also combine it with compute capability settings for specific GPU architectures: ```lua target("cuda_app") set_kind("binary") add_files("src/*.cu") add_rules("cuda") set_values("cuda.sdkver", "12.0") set_values("cuda.arch", "sm_75", "sm_80", "sm_86") ``` Different targets can use different CUDA versions: ```lua -- Target using CUDA 11.8 target("cuda11_app") set_kind("binary") add_files("src/cuda11/*.cu") add_rules("cuda") set_values("cuda.sdkver", "11.8") -- Target using CUDA 12.0 target("cuda12_app") set_kind("binary") add_files("src/cuda12/*.cu") add_rules("cuda") set_values("cuda.sdkver", "12.0") ``` This feature is particularly useful for: * Projects requiring specific CUDA versions for compatibility * Multi-version CUDA development environments * Ensuring consistent CUDA compilation across different systems * Targeting specific GPU architectures with appropriate CUDA features For more details, see: [#6964](https://github.com/xmake-io/xmake/pull/6964) ### Add GCC 15 toolchain support We have added support for the latest GCC 15 toolchain, ensuring compatibility with the newest compiler features and improvements. ```bash $ xmake f --toolchain=gcc-15 ``` For more details, see: [#6929](https://github.com/xmake-io/xmake/pull/6929) ### Add async support for os APIs We have added asynchronous support for os APIs, allowing non-blocking file and process operations in xmake scripts. This enables concurrent I/O operations, significantly improving performance when dealing with multiple file operations or long-running processes. **Supported APIs:** The following APIs now support async operations: * `os.rm` - Remove files * `os.rmdir` - Remove directories * `os.cp` - Copy files * `os.files` - Find files * `os.filedirs` - Find files and directories * `os.dirs` - Find directories * `os.match` - Match file patterns * `lib.detect.find_file` - Find file * `lib.detect.find_library` - Find library * `lib.detect.find_path` - Find path * `lib.detect.find_directory` - Find directory **Async modes:** There are two async modes available: 1. **`async = true`** (blocking): The operation can be scheduled with other coroutine tasks. You need to wait for the return value. 2. **`async = true, detach = true`** (non-blocking): The operation executes in a background thread, so you don't need to wait for the return value. **Usage examples:** ```lua import("lib.detect.find_file") function main() -- Remove file in an idle background thread, we don't need to wait for it os.rm("/tmp/xxx.txt", {async = true, detach = true}) -- Async wait and get return value local files = os.files("src/*.c", {async = true}) -- Async wait and find file local file = find_file("*.txt", "/tmp/", {async = true}) end ``` This enables non-blocking I/O operations, significantly improves performance when reading multiple configuration files in parallel or processing large file lists concurrently. It's also useful for running external tools asynchronously, implementing parallel build steps, and improving plugin and rule performance. For more details, see: [#6989](https://github.com/xmake-io/xmake/pull/6989) and [#6868](https://github.com/xmake-io/xmake/issues/6868) ## Improvements ### Improve toolchain configuration syntax We have improved the toolchain configuration syntax to support inline configuration options, making toolchain setup more concise and declarative. The new syntax simplifies toolchain configuration with three main formats: **Simplified toolchain config formats:** 1. **Only toolchain name**: `clang`, `gcc` 2. **Toolchain@package**: `clang@llvm-10`, `@muslcc`, `zig` 3. **Toolchain\[configs]@package**: `mingw[clang]@llvm-mingw`, `msvc[vs=2025,..]` **Fast toolchain config switching:** You can now quickly switch toolchain configurations using inline syntax: ```lua -- Equivalent to: set_toolchains("mingw", {clang = true}) set_toolchains("mingw[clang]") -- Command line usage -- xmake f --toolchain=mingw[clang] ``` **Examples:** ```lua -- Simple toolchain set_toolchains("clang") -- Toolchain with package set_toolchains("clang@llvm-10") set_toolchains("@muslcc") set_toolchains("zig") -- Toolchain with configs and package set_toolchains("mingw[clang]@llvm-mingw") set_toolchains("msvc[vs=2025]") -- Multiple configs set_toolchains("mingw[clang]", {sdk = "/path/to/llvm-mingw"}) ``` **Additional improvements:** * Added clang support for llvm-mingw toolchain: ```bash xmake f --toolchain=mingw[clang] --sdk=/xxx/llvm-mingw ``` ```lua set_toolchains("mingw[clang]@llvm-mingw") ``` * Added gcc support for old version NDK toolchain: ```bash xmake f -p android --toolchain=ndk[gcc] --ndk=/xxxx ``` This makes toolchain configuration more concise and readable, enables declarative toolchain setup, and makes it easier to manage multiple toolchain variants. It's particularly useful for quick toolchain switching, per-target toolchain configuration, CI/CD pipeline setup, and cross-compilation toolchain specification. For more details, see: [#6924](https://github.com/xmake-io/xmake/pull/6924), [discussion #6903](https://github.com/xmake-io/xmake/discussions/6903), and [discussion #6879](https://github.com/xmake-io/xmake/discussions/6879) ### Improve file reading performance We have significantly improved file reading performance, especially for large files and projects with many source files. The improvements include better buffering strategies and optimized I/O operations. For more details, see: [#6942](https://github.com/xmake-io/xmake/pull/6942) ### Add realtime output support for xmake test We have added realtime output support for `xmake test`, allowing test output to be displayed in real-time as tests run, rather than buffering output until the test completes. This is particularly useful for long-running tests or tests that produce continuous output, as it provides immediate feedback on test progress. To enable realtime output for a test, set `realtime_output = true` in the test configuration: ```lua target("test") set_kind("binary") add_tests("stub_n", {realtime_output = true, files = "tests/stub_n*.cpp", defines = "STUB_N"}) ``` When `realtime_output` is enabled, the test output will be streamed directly to the terminal as the test runs, making it easier to monitor test progress and debug issues in real-time. For more details, see: [#6993](https://github.com/xmake-io/xmake/pull/6993) ### Improve TTY handling and output We have improved TTY handling and output formatting in the `core.base.tty` module. The following new interfaces have been added: * `tty.cursor_move_up(n)` / `tty.cursor_move_down(n)` - Move cursor vertically * `tty.cursor_move_left(n)` / `tty.cursor_move_right(n)` - Move cursor horizontally * `tty.cursor_move_to_col(n)` - Move cursor to specific column * `tty.cursor_save()` / `tty.cursor_restore()` - Save and restore cursor position * `tty.cursor_hide()` / `tty.cursor_show()` - Control cursor visibility * `tty.cr()` - Move to start of line (carriage return) * `tty.erase_line()` - Clear entire line * `tty.erase_line_to_end()` - Erase from cursor to end of line * `tty.has_vtansi()` - Check if terminal supports ANSI control codes For more details, see: [#6970](https://github.com/xmake-io/xmake/pull/6970) ### Add Ghostty terminal detection support We have added support for detecting the Ghostty terminal, ensuring proper output formatting and color support in this modern terminal emulator. For more details, see: [#6987](https://github.com/xmake-io/xmake/pull/6987) ### Improve graph module performance We have improved the performance of the graph module, which is used for dependency resolution and build graph generation. The improvements result in faster project configuration and dependency analysis. For more details, see: [#7027](https://github.com/xmake-io/xmake/pull/7027) ## Changelog ### New features * [#6929](https://github.com/xmake-io/xmake/pull/6929): Add support for GCC 15 toolchain * [#6967](https://github.com/xmake-io/xmake/pull/6967): Add Swift interop support for C++ and Objective-C * [#6964](https://github.com/xmake-io/xmake/pull/6964): Support specifying CUDA SDK version via cuda\_sdkver * [#6963](https://github.com/xmake-io/xmake/pull/6963): Add libtool patch support for cross compilation * [#6974](https://github.com/xmake-io/xmake/pull/6974): Support multi-row refresh for progress output * [#7024](https://github.com/xmake-io/xmake/pull/7024): Add JSON output format for `xmake show -t target` * [#7025](https://github.com/xmake-io/xmake/pull/7025): Add XML module with parsing and encoding support * [#6989](https://github.com/xmake-io/xmake/pull/6989): Add async support for os APIs ### Changes * [#6924](https://github.com/xmake-io/xmake/pull/6924): Improve toolchain configuration with add\_toolchains("name\[configs]") syntax * [#6942](https://github.com/xmake-io/xmake/pull/6942): Improve file reading performance * [#6970](https://github.com/xmake-io/xmake/pull/6970): Improve TTY handling and output * [#6977](https://github.com/xmake-io/xmake/pull/6977): Refactor Xcode toolchain and integrate it into LLVM toolchain for Apple devices * [#6987](https://github.com/xmake-io/xmake/pull/6987): Add Ghostty terminal detection support * [#7003](https://github.com/xmake-io/xmake/pull/7003): Limit build environment retrieval in package configurations * [#7004](https://github.com/xmake-io/xmake/pull/7004): Skip rebuilding packages and std modules when using -r flag * [#7019](https://github.com/xmake-io/xmake/pull/7019): Improve xmake.sh/configure script and add Ninja generator support * [#7022](https://github.com/xmake-io/xmake/pull/7022): Make zig-cc toolchain inherit from clang * [#7027](https://github.com/xmake-io/xmake/pull/7027): Improve graph module performance * [#7031](https://github.com/xmake-io/xmake/pull/7031): Improve require parsing * [#7032](https://github.com/xmake-io/xmake/pull/7032): Improve symbol extraction ### Bugs fixed * [#6926](https://github.com/xmake-io/xmake/pull/6926): Fix loading Unicode main script path on Windows * [#6931](https://github.com/xmake-io/xmake/pull/6931): Fix C++ modules: fallback to system-wide clang-scan-deps when toolchain version is not installed * [#6937](https://github.com/xmake-io/xmake/pull/6937): Fix target jobs handling * [#6954](https://github.com/xmake-io/xmake/pull/6954): Fix modules support for vsxmake/vs generators * [#6955](https://github.com/xmake-io/xmake/pull/6955): Fix build number sorting in packages * [#6956](https://github.com/xmake-io/xmake/pull/6956): Fix build failure when using zigcc linker that doesn't support depfile * [#6959](https://github.com/xmake-io/xmake/pull/6959): Fix using zigcc with autotools for dynamic linking * [#6983](https://github.com/xmake-io/xmake/pull/6983): Fix modules: strip sanitizer flags for module reuse * [#6984](https://github.com/xmake-io/xmake/pull/6984): Fix libdir path in installed CMake import files * [#6992](https://github.com/xmake-io/xmake/pull/6992): Fix modules: add all supported platforms for clang get\_cpp\_library\_name * [#6993](https://github.com/xmake-io/xmake/pull/6993): Fix xmake test modules * [#6996](https://github.com/xmake-io/xmake/pull/6996): Fix Nimble find\_package to use latest package list format * [#6999](https://github.com/xmake-io/xmake/pull/6999): Fix rootdir handling * [#7002](https://github.com/xmake-io/xmake/pull/7002): Fix asn1c: include generated output as system headers * [#7012](https://github.com/xmake-io/xmake/pull/7012): Fix sparse checkout handling * [#7013](https://github.com/xmake-io/xmake/pull/7013): Fix removing dependencies when packaging * [#7016](https://github.com/xmake-io/xmake/pull/7016): Fix project default configuration in vsxmake * [#7017](https://github.com/xmake-io/xmake/pull/7017): Fix lock\_packages typo * [#7018](https://github.com/xmake-io/xmake/pull/7018): Fix build order: only disable when dependency linking inheritance is disabled --- --- url: /zh/posts/xmake-update-v3.0.5.md --- ## 新特性介绍 {#new-features} 新版本中,我们引入了多个重要特性,显著提升了开发体验。重点包括**多行进度输出**(支持主题配置,提供更好的构建可见性)、全面的**XML 模块**(用于解析和编码 XML 数据)、**异步 OS API**(提升 I/O 性能)以及**Swift 互操作支持**(实现 Swift 与 C++/Objective-C 代码的无缝集成)。同时,我们也对工具链配置、TTY 处理进行了重大改进,并进行了各种性能优化。 ### 支持多行刷新进度输出 {#support-multi-row-refresh-progress-output} 我们改进了进度输出,支持多行刷新,在长时间运行的构建过程中提供显著更好的视觉体验。构建输出现在不再只更新单行进度,而是显示多个并发构建任务及其各自的进度,使得监控并行编译变得更加容易。 输出现在显示并行构建的多行进度,每个编译任务都有实时状态更新: ![progress](/assets/img/progress-multirow.png) 您可以通过两种方式启用多行进度输出: 1. **通过主题配置**:使用 `soong` 主题,它默认包含多行进度: ```bash $ xmake g --theme=soong ``` 2. **通过项目策略**:在 `xmake.lua` 中直接启用: ```lua set_policy("build.progress_style", "multirow") ``` 这提供了更好的并行构建进度可见性,更容易识别编译缓慢的单元,并为包含大量源文件或具有多个编译单元的并行构建的大型项目改善了整体用户体验。 更多详情,请参考:[#6974](https://github.com/xmake-io/xmake/pull/6974) ### 添加 Swift 与 C++/Objective-C 互操作支持 {#add-swift-interop-support} 我们新增了全面的 Swift 互操作支持,实现了 Swift 与 C++/Objective-C 代码之间的无缝双向互操作性。当设置了 `swift.interop` 目标值时,`swift.interop` 规则会自动启用,使得在同一个项目中混合使用 Swift 和 C++ 代码变得非常容易。 Swift 互操作支持包括: * Swift-C++ 双向互操作性 * 自动生成 C++ 头文件,使 C++ 可以调用 Swift 函数 * 支持 Objective-C 和 C++ 两种互操作模式 * Swift 静态库归档工具集,增强编译工作流 **目标值配置:** 您可以使用以下目标值来配置 Swift 互操作: ```lua set_values("swift.modulename", "SwiftFibonacci") -- 设置 Swift 模块名 set_values("swift.interop", "cxx") -- 启用互操作:"objc" 或 "cxx" set_values("swift.interop.headername", "fibonacci-Swift.h") -- 定义输出头文件名 set_values("swift.interop.cxxmain", true) -- 强制 -parse-as-library 以避免重复的 main 符号 ``` **完整示例:Swift-C++ 互操作** 以下是一个完整的示例,演示 Swift-C++ 双向互操作: **fibonacci.swift:** ```swift // fibonacci.swift public func fibonacciSwift(_ x: CInt) -> CInt { print("x [swift]: \(x)") if x <= 1 { return 1 } return fibonacciSwift(x - 1) + fibonacciSwift(x - 2) } ``` **main.cpp:** ```cpp // main.cpp #include #include int main(int argc, char ** argv) { std::cout << SwiftFibonacci::fibonacciSwift(5) << std::endl; return 0; } ``` **xmake.lua:** ```lua -- xmake.lua target("cxx_interop") set_kind("binary") set_languages("cxx20") add_files("lib/**.swift", {public = true}) add_files("src/**.cpp") set_values("swift.modulename", "SwiftFibonacci") set_values("swift.interop", "cxx") set_values("swift.interop.headername", "fibonacci-Swift.h") set_values("swift.interop.cxxmain", true) ``` **构建输出:** ```bash $ xmake checking for platform ... macosx checking for architecture ... x86_64 checking for Xcode directory ... /Applications/Xcode.app [ 3%]: generating.swift.header fibonacci-Swift.h [ 38%]: cache compiling.release src/fibonacci.cpp [ 56%]: compiling.release lib/fibonacci/fibonacci.swift [ 76%]: linking.release cxx_interop [100%]: build ok, spent 1.785s $ xmake run x [swift]: 5 x [swift]: 4 ... 8 ``` 当设置 `swift.interop` 时,xmake 会自动生成 C++ 头文件,使 C++ 代码能够调用 Swift 函数。您可以使用 `swift.modulename` 定义 Swift 模块名,该名称将成为 C++ 中的命名空间。选择 `"objc"` 进行 Objective-C 互操作,或选择 `"cxx"` 进行 C++ 互操作。当 Swift 和 C++ 都有 main 函数时,使用 `swift.interop.cxxmain` 来避免重复的 main 符号。 这个特性特别适用于: * 逐步将现有 C++ 项目迁移到 Swift * 在 Swift 应用程序中使用高性能 C++ 库 * 为 C++ API 创建 Swift 包装器 * 构建利用两种语言的跨平台应用程序 * 从 C++ 应用程序无缝调用 Swift 代码 更多详情,请参考:[#6967](https://github.com/xmake-io/xmake/pull/6967) ### 添加 XML 模块支持 {#add-xml-module-support} 我们引入了一个新的 `core.base.xml` 模块,提供了一个轻量级的 DOM 风格 XML 工具包,可在 Xmake 的沙箱环境中工作。它专注于可预测的数据结构、类似 JSON 的易用性,以及可选的流式解析,使您可以在不构建整个树的情况下解析大型 XML 文档。 XML 模块特性: * 使用普通 Lua 表的 DOM 风格节点结构 * 用于大文件的流式解析器(`xml.scan`) * 类似 XPath 的查询(`xml.find`) * 便捷的文件 I/O 辅助函数(`xml.loadfile`,`xml.savefile`) * 支持注释、CDATA、DOCTYPE 和未引用的属性 * 可自定义缩进的格式化输出 **节点结构:** XML 节点是具有以下结构的普通 Lua 表: ```lua { name = "element-name" | nil, -- 仅用于元素节点 kind = "element" | "text" | "comment" | "cdata" | "doctype" | "document", attrs = { key = value, ... } or nil, text = string or nil, children = { child1, child2, ... } or nil, prolog = { comment/doctype nodes before root } or nil } ``` **基本用法:** ```lua import("core.base.xml") -- 解析 XML 字符串 local doc = assert(xml.decode([[ hello ]])) -- 查找并修改节点 local item = assert(xml.find(doc, "//item[@id='foo']")) item.attrs.lang = "en" -- 直接修改属性 item.children = {xml.text("world")} -- 替换现有文本节点 -- 添加注释 table.insert(doc.children, xml.comment("generated by xmake")) -- 使用格式化输出编码 local pretty = assert(xml.encode(doc, {pretty = true})) assert(xml.savefile("out.xml", doc, {pretty = true})) ``` **文件操作:** ```lua import("core.base.xml") -- 从文件加载 local plist = assert(xml.loadfile("Info.plist")) -- 修改并保存 local dict = assert(xml.find(plist, "plist/dict")) -- ... 修改节点 ... assert(xml.savefile("Info.plist", plist, {pretty = true, indent = 2})) ``` **用于大文件的流式解析器:** ```lua import("core.base.xml") local found xml.scan(plist_text, function(node) if node.name == "key" and xml.text_of(node) == "NSPrincipalClass" then found = node return false -- 提前终止 end end) ``` `xml.scan` 在节点完成时遍历它们;返回 `false` 会立即停止扫描。这对于只需要几个条目的大文件非常理想。 **类似 XPath 的查询:** ```lua import("core.base.xml") local doc = assert(xml.loadfile("config.xml")) -- 通过路径查找 local element = xml.find(doc, "/root/item") -- 通过属性查找 local item = xml.find(doc, "//item[@id='foo']") -- 通过文本内容查找 local node = xml.find(doc, "//string[text()='value']") -- 获取文本内容 local text = xml.text_of(node) ``` **节点创建辅助函数:** ```lua import("core.base.xml") local textnode = xml.text("hello") local empty = xml.empty("br", {class = "line"}) local comment = xml.comment("generated by xmake") local cdata_node = xml.cdata("if (value < 1) {...}") local doctype = xml.doctype('plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"') ``` **选项:** | 选项 | 适用于 | 描述 | |------|--------|------| | `trim_text = true` | `xml.decode`, `xml.scan` | 去除文本节点中的前导/尾随空格 | | `keep_whitespace_nodes = true` | `xml.decode`, `xml.scan` | 保留仅包含空白字符的文本节点 | | `pretty = true` / `indent` / `indentchar` | `xml.encode`, `xml.savefile` | 启用格式化并控制缩进 | **使用场景:** * 读取和修改 IDE 项目配置(Info.plist、.vcxproj 等) * 生成基于 XML 的项目文件 * 处理构建元数据和报告 * 使用流式解析高效解析大型 XML 文件 * 在 XML 和 Lua 数据结构之间转换 更多详情,请参考:[#7025](https://github.com/xmake-io/xmake/pull/7025) ### 添加目标信息的 JSON 输出格式 {#add-json-output-format-for-target-info} 我们为 `xmake show` 命令添加了 JSON 输出格式支持,使得以编程方式提取目标信息变得更加容易。这个特性实现了与 IDE、构建自动化工具和需要解析 xmake 项目元数据的自定义脚本的无缝集成。 JSON 输出包含全面的目标信息: * 目标名称、类型和文件路径 * 源文件和头文件 * 编译器标志和宏定义 * 链接库和链接器标志 * 包含目录和依赖项 * 配置选项和规则 您可以使用 `--json` 获得紧凑输出,或使用 `--pretty-json` 获得格式化输出: ```bash $ xmake show -t target --json {"targets":[{"name":"test","kind":"binary","files":["src/main.cpp"],"links":["pthread"],"defines":["DEBUG"]}]} $ xmake show -t target --pretty-json { "targets": [ { "name": "test", "kind": "binary", "files": ["src/main.cpp"], "links": ["pthread"], "defines": ["DEBUG"], "includedirs": ["include"], "cxxflags": ["-std=c++17"], "deps": ["mylib"] } ] } ``` 您可以提取目标信息用于 IDE 集成或在脚本中使用: ```bash # 提取目标信息用于 IDE 集成 xmake show -t target --pretty-json > project_info.json # 在脚本中使用 TARGET_INFO=$(xmake show -t target --json) TARGET_NAME=$(echo $TARGET_INFO | jq -r '.targets[0].name') ``` 这对于以下场景特别有用: * IDE 集成(VS Code、CLion 等) * 自动化构建系统和 CI/CD 流水线 * 自定义项目分析工具 * 文档生成 更多详情,请参考:[#7024](https://github.com/xmake-io/xmake/pull/7024) ### 支持指定 CUDA SDK 版本 {#support-specify-cuda-sdk-version} 我们添加了通过 `cuda_sdkver` 配置选项指定 CUDA SDK 版本的支持,让您对 CUDA 编译有精确的控制。这在处理多个 CUDA 安装或需要针对特定 CUDA 版本以确保兼容性时非常重要。 您可以为目标指定 CUDA SDK 版本: ```lua target("cuda_app") set_kind("binary") add_files("src/*.cu") add_rules("cuda") set_values("cuda.sdkver", "12.0") -- 指定 CUDA SDK 版本 ``` 您也可以将其与针对特定 GPU 架构的计算能力设置结合使用: ```lua target("cuda_app") set_kind("binary") add_files("src/*.cu") add_rules("cuda") set_values("cuda.sdkver", "12.0") set_values("cuda.arch", "sm_75", "sm_80", "sm_86") ``` 不同的目标可以使用不同的 CUDA 版本: ```lua -- 使用 CUDA 11.8 的目标 target("cuda11_app") set_kind("binary") add_files("src/cuda11/*.cu") add_rules("cuda") set_values("cuda.sdkver", "11.8") -- 使用 CUDA 12.0 的目标 target("cuda12_app") set_kind("binary") add_files("src/cuda12/*.cu") add_rules("cuda") set_values("cuda.sdkver", "12.0") ``` 这个特性特别适用于: * 需要特定 CUDA 版本以确保兼容性的项目 * 多版本 CUDA 开发环境 * 确保在不同系统上一致的 CUDA 编译 * 针对特定 GPU 架构使用相应的 CUDA 特性 更多详情,请参考:[#6964](https://github.com/xmake-io/xmake/pull/6964) ### 添加 GCC 15 工具链支持 {#add-gcc-15-toolchain-support} 我们添加了对最新 GCC 15 工具链的支持,确保与最新的编译器特性和改进的兼容性。 ```bash $ xmake f --toolchain=gcc-15 ``` 更多详情,请参考:[#6929](https://github.com/xmake-io/xmake/pull/6929) ### 添加 os API 异步支持 {#add-os-api-async-support} 我们为 os API 添加了异步支持,允许在 xmake 脚本中进行非阻塞的文件和进程操作。这使得能够并发执行 I/O 操作,在处理多个文件操作或长时间运行的进程时显著提高性能。 **支持的 API:** 以下 API 现在支持异步操作: * `os.rm` - 删除文件 * `os.rmdir` - 删除目录 * `os.cp` - 复制文件 * `os.files` - 查找文件 * `os.filedirs` - 查找文件和目录 * `os.dirs` - 查找目录 * `os.match` - 匹配文件模式 * `lib.detect.find_file` - 查找文件 * `lib.detect.find_library` - 查找库 * `lib.detect.find_path` - 查找路径 * `lib.detect.find_directory` - 查找目录 **异步模式:** 有两种异步模式可用: 1. **`async = true`**(阻塞):操作可以与其他协程任务一起调度。您需要等待返回值。 2. **`async = true, detach = true`**(非阻塞):操作在后台线程中执行,因此您不需要等待返回值。 **使用示例:** ```lua import("lib.detect.find_file") function main() -- 在空闲的后台线程中删除文件,我们不需要等待它 os.rm("/tmp/xxx.txt", {async = true, detach = true}) -- 异步等待并获取返回值 local files = os.files("src/*.c", {async = true}) -- 异步等待并查找文件 local file = find_file("*.txt", "/tmp/", {async = true}) end ``` 这实现了非阻塞 I/O 操作,在并行读取多个配置文件或并发处理大型文件列表时显著提高性能。它对于异步运行外部工具、实现并行构建步骤以及提高插件和规则性能也很有用。 更多详情,请参考:[#6989](https://github.com/xmake-io/xmake/pull/6989) 和 [#6868](https://github.com/xmake-io/xmake/issues/6868) ## 改进 {#improvements} ### 改进工具链配置语法 {#improve-toolchain-config-syntax} 我们改进了工具链配置语法,支持内联配置选项,使工具链设置更加简洁和声明式。新语法提供了三种主要的简化格式: **简化的工具链配置格式:** 1. **仅工具链名称**:`clang`、`gcc` 2. **工具链@包**:`clang@llvm-10`、`@muslcc`、`zig` 3. **工具链\[配置]@包**:`mingw[clang]@llvm-mingw`、`msvc[vs=2025,..]` **快速切换工具链配置:** 您现在可以使用内联语法快速切换工具链配置: ```lua -- 等价于:set_toolchains("mingw", {clang = true}) set_toolchains("mingw[clang]") -- 命令行用法 -- xmake f --toolchain=mingw[clang] ``` **示例:** ```lua -- 简单工具链 set_toolchains("clang") -- 带包的工具链 set_toolchains("clang@llvm-10") set_toolchains("@muslcc") set_toolchains("zig") -- 带配置和包的工具链 set_toolchains("mingw[clang]@llvm-mingw") set_toolchains("msvc[vs=2025]") -- 多个配置 set_toolchains("mingw[clang]", {sdk = "/path/to/llvm-mingw"}) ``` **其他改进:** * 为 llvm-mingw 工具链添加了 clang 支持: ```bash xmake f --toolchain=mingw[clang] --sdk=/xxx/llvm-mingw ``` ```lua set_toolchains("mingw[clang]@llvm-mingw") ``` * 为旧版本 NDK 工具链添加了 gcc 支持: ```bash xmake f -p android --toolchain=ndk[gcc] --ndk=/xxxx ``` 这使得工具链配置更加简洁和可读,支持声明式工具链设置,并更易于管理多个工具链变体。它对于快速切换工具链、每个目标的工具链配置、CI/CD 流水线设置以及交叉编译工具链规范特别有用。 更多详情,请参考:[#6924](https://github.com/xmake-io/xmake/pull/6924)、[讨论 #6903](https://github.com/xmake-io/xmake/discussions/6903) 和 [讨论 #6879](https://github.com/xmake-io/xmake/discussions/6879) ### 改进文件读取性能 {#improve-file-reading-performance} 我们显著改进了文件读取性能,特别是对于大文件和包含大量源文件的项目。 改进包括更好的缓冲策略和优化的 I/O 操作。 更多详情,请参考:[#6942](https://github.com/xmake-io/xmake/pull/6942) ### 添加 xmake test 实时输出支持 {#add-xmake-test-realtime-output-support} 我们为 `xmake test` 添加了实时输出支持,允许测试输出在测试运行时实时显示,而不是等到测试完成后再缓冲输出。这对于长时间运行的测试或产生连续输出的测试特别有用,因为它提供了即时的测试进度反馈。 要为测试启用实时输出,在测试配置中设置 `realtime_output = true`: ```lua target("test") set_kind("binary") add_tests("stub_n", {realtime_output = true, files = "tests/stub_n*.cpp", defines = "STUB_N"}) ``` 当启用 `realtime_output` 时,测试输出将在测试运行时直接流式传输到终端,使得实时监控测试进度和调试问题变得更加容易。 更多详情,请参考:[#6993](https://github.com/xmake-io/xmake/pull/6993) ### 改进 TTY 处理和输出 {#improve-tty-handling-and-output} 我们改进了 `core.base.tty` 模块的 TTY 处理和输出格式。新增了以下接口: * `tty.cursor_move_up(n)` / `tty.cursor_move_down(n)` - 垂直移动光标 * `tty.cursor_move_left(n)` / `tty.cursor_move_right(n)` - 水平移动光标 * `tty.cursor_move_to_col(n)` - 移动到指定列 * `tty.cursor_save()` / `tty.cursor_restore()` - 保存和恢复光标位置 * `tty.cursor_hide()` / `tty.cursor_show()` - 控制光标可见性 * `tty.cr()` - 移动到行首(回车) * `tty.erase_line()` - 清除整行 * `tty.erase_line_to_end()` - 清除从光标到行尾 * `tty.has_vtansi()` - 检查终端是否支持 ANSI 控制码 更多详情,请参考:[#6970](https://github.com/xmake-io/xmake/pull/6970) ### 添加 Ghostty 终端检测支持 {#add-ghostty-terminal-detection-support} 我们添加了对 Ghostty 终端的检测支持,确保在这个现代终端模拟器中具有正确的输出格式和颜色支持。 更多详情,请参考:[#6987](https://github.com/xmake-io/xmake/pull/6987) ### 改进图模块性能 {#improve-graph-module-performance} 我们改进了图模块的性能,该模块用于依赖解析和构建图生成。 这些改进使得项目配置和依赖分析速度更快。 更多详情,请参考:[#7027](https://github.com/xmake-io/xmake/pull/7027) ## 更新日志 {#changelog} ### 新特性 {#new-features-list} * [#6929](https://github.com/xmake-io/xmake/pull/6929): 添加 GCC 15 工具链支持 * [#6967](https://github.com/xmake-io/xmake/pull/6967): 添加 Swift 与 C++/Objective-C 互操作支持 * [#6964](https://github.com/xmake-io/xmake/pull/6964): 支持通过 cuda\_sdkver 指定 CUDA SDK 版本 * [#6963](https://github.com/xmake-io/xmake/pull/6963): 添加交叉编译的 libtool 补丁支持 * [#6974](https://github.com/xmake-io/xmake/pull/6974): 支持多行刷新进度输出 * [#7024](https://github.com/xmake-io/xmake/pull/7024): 为 `xmake show -t target` 添加 JSON 输出格式 * [#7025](https://github.com/xmake-io/xmake/pull/7025): 添加 XML 模块,支持解析和编码 * [#6989](https://github.com/xmake-io/xmake/pull/6989): 添加 os API 异步支持 ### 改进 {#improvements-list} * [#6924](https://github.com/xmake-io/xmake/pull/6924): 改进工具链配置,支持 add\_toolchains("name\[configs]") 语法 * [#6942](https://github.com/xmake-io/xmake/pull/6942): 改进文件读取性能 * [#6970](https://github.com/xmake-io/xmake/pull/6970): 改进 TTY 处理和输出 * [#6977](https://github.com/xmake-io/xmake/pull/6977): 重构 Xcode 工具链,集成到 Apple 设备的 LLVM 工具链中 * [#6987](https://github.com/xmake-io/xmake/pull/6987): 添加 Ghostty 终端检测支持 * [#7003](https://github.com/xmake-io/xmake/pull/7003): 限制包配置中的构建环境获取 * [#7004](https://github.com/xmake-io/xmake/pull/7004): 使用 -r 标志时跳过重建包和 std 模块 * [#7019](https://github.com/xmake-io/xmake/pull/7019): 改进 xmake.sh/configure 脚本,添加 Ninja 生成器支持 * [#7022](https://github.com/xmake-io/xmake/pull/7022): 使 zig-cc 工具链继承自 clang * [#7027](https://github.com/xmake-io/xmake/pull/7027): 改进图模块性能 * [#7031](https://github.com/xmake-io/xmake/pull/7031): 改进 require 解析 * [#7032](https://github.com/xmake-io/xmake/pull/7032): 改进符号提取 ### Bugs 修复 {#bug-fixes} * [#6926](https://github.com/xmake-io/xmake/pull/6926): 修复 Windows 上加载 Unicode 主脚本路径的问题 * [#6931](https://github.com/xmake-io/xmake/pull/6931): 修复 C++ 模块:当工具链版本未安装时回退到系统范围的 clang-scan-deps * [#6937](https://github.com/xmake-io/xmake/pull/6937): 修复目标作业处理 * [#6954](https://github.com/xmake-io/xmake/pull/6954): 修复 vsxmake/vs 生成器的模块支持 * [#6955](https://github.com/xmake-io/xmake/pull/6955): 修复包中的构建号排序 * [#6956](https://github.com/xmake-io/xmake/pull/6956): 修复使用不支持 depfile 的 zigcc 链接器时的构建失败 * [#6959](https://github.com/xmake-io/xmake/pull/6959): 修复使用 zigcc 与 autotools 进行动态链接的问题 * [#6983](https://github.com/xmake-io/xmake/pull/6983): 修复模块:为模块重用去除 sanitizer 标志 * [#6984](https://github.com/xmake-io/xmake/pull/6984): 修复已安装的 CMake 导入文件中的 libdir 路径 * [#6992](https://github.com/xmake-io/xmake/pull/6992): 修复模块:为 clang get\_cpp\_library\_name 添加所有支持的平台 * [#6993](https://github.com/xmake-io/xmake/pull/6993): 修复 xmake test 模块 * [#6996](https://github.com/xmake-io/xmake/pull/6996): 修复 Nimble find\_package 以使用最新的包列表格式 * [#6999](https://github.com/xmake-io/xmake/pull/6999): 修复 rootdir 处理 * [#7002](https://github.com/xmake-io/xmake/pull/7002): 修复 asn1c:将生成的输出作为系统头文件包含 * [#7012](https://github.com/xmake-io/xmake/pull/7012): 修复稀疏检出处理 * [#7013](https://github.com/xmake-io/xmake/pull/7013): 修复打包时移除依赖的问题 * [#7016](https://github.com/xmake-io/xmake/pull/7016): 修复 vsxmake 中的项目默认配置 * [#7017](https://github.com/xmake-io/xmake/pull/7017): 修复 lock\_packages 拼写错误 * [#7018](https://github.com/xmake-io/xmake/pull/7018): 修复构建顺序:仅在禁用依赖链接继承时禁用 --- --- url: /zh/blog.md --- # Xmake 博客 Xmake 团队的更新、技巧和观点。 --- --- url: /zh/posts/xmake-course-build-c-projects.md --- ## 实验楼课程 [Xmake 带你轻松构建 C/C++ 项目](https://www.lanqiao.cn/courses/2764) 是我们在实验楼上推出的一门 xmake 入门和进阶课程(收费),以边学边做实验的方式快速学习 xmake 的使用。 通过此处优惠码购买可享 9 折优惠:`NYFbmf3X` ### xmake 介绍 [Xmake](https://xmake.io) 是一个基于 Lua 的轻量级跨平台 C/C++ 构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt 而言,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门 C/C++ 项目的编译开发,提供一站式跨平台编译、运行、调试、打包、安装等操作,能够让大家把更多的精力集中在实际的项目开发上。 虽然,简单易用是 xmake 的一大特色,但 xmake 的功能也是非常强大的,既能够像 make/ninja 那样直接编译项目,也可以像 cmake/meson 那样生成工程文件,还有内置的包管理系统来帮助大家解决 C/C++ 依赖库的集成使用问题。 ### 课程介绍 本课程以循序渐进的方式,带你入门和进阶 xmake,从最基础的编译配置,到复杂项目的定制化组织和维护,在课程最后几节,我们还通过实战的方式,一步步带你体验第三方 C/C++ 项目的移植编译,以及 vscode/xmake 集成环境的可视化编译操作流程。最后一节实验中,我们还会讲解如何使用 xmake 和 vscode 去编译开发基于 Qt 的应用程序。 ### 你将学到 | | | | --- | --- | | C/C++ 程序的编译运行和调试 | xmake 基础命令使用 | | C/C++ 依赖库集成和使用 | 大型工程结构的维护 | | xmake 复杂脚本和规则的编写 | 如何实现跨平台编译 | | xmake 插件开发 | VS Code/xmake 集成环境的使用 | | xmake 的基础语法 | C/C++ 项目基础编译配置 | | 多个目标程序的依赖编译 | 预编译头文件的设置 | | cmake/makefile 工程文件的生成 | xmake 脚本运行插件的使用 | | C/C++ 代码的移植编译 | Qt 项目程序的编译开发 | ### 课程链接 链接地址: 我们也可以通过扫描下方二维码进入课程: ![](/assets/img/xmake_course.png) --- --- url: /zh/posts/api-scope.md --- xmake的工程描述文件xmake.lua虽然基于lua语法,但是为了使得更加方便简洁得编写项目构建逻辑,xmake对其进行了一层封装,使得编写xmake.lua不会像些makefile那样繁琐 基本上写个简单的工程构建描述,只需三行就能搞定,例如: ```lua target("test") set_kind("binary") add_files("src/*.c") ``` 然后只需要执行编译并且运行它: ```bash $ xmake run test ``` 这对于想要临时写些测试代码来讲,极大地提升了开发效率。。 ### 作用域与工程描述语法 xmake的描述语法是按作用域划分的,主要分为: * 外部作用域 * 内部作用域 那哪些属于外部,哪些又属于内部呢,看看下面的注释,就知道个大概了: ```lua -- 外部作用域 target("test") -- 外部作用域 set_kind("binary") add_files("src/*.c") on_run(function () -- 内部作用域 end) after_package(function () -- 内部作用域 end) -- 外部作用域 task("hello") -- 外部作用域 on_run(function () -- 内部作用域 end) ``` 简单的说,就是在自定义脚本`function () end`之内的都属于内部作用域,也就是脚本作用域,其他地方都是都属于于外部作用域。。 #### 外部作用域 对于大部分工程来说,并不需要很复杂的工程描述,也不需要自定义脚本支持,只需要简单的 `set_xxx` 或者 `add_xxx` 就能满足需求了 那么根据二八定律,80%的情况下,我们只需要这么写: ```lua target("test") set_kind("static") add_files("src/test/*.c") target("demo") add_deps("test") set_kind("binary") add_links("test") add_files("src/demo/*.c") ``` 不需要复杂的api调用,也不需要各种繁琐的变量定义,以及 `if` 判断 和 `for` 循环,要的就是简洁可读,一眼看过去,就算不懂lua语法也没关系 就当做简单的描述语法,看上去有点像函数调用而已,会点编程的基本一看就知道怎么配置。 为了做到简洁、安全,在这个作用域内,很多lua 内置api是不开放出来的,尤其是跟写文件、修改操作环境相关的,仅仅提供一些基本的只读接口,和逻辑操作 目前外部作用域开放的lua内置api有: * table * string * pairs * ipairs * print:修改版,提供格式化打印支持 * os:仅提供只读接口,例如getenv等等 当然虽然内置lua api提供不多,但xmake还提供了很多扩展api,像描述api就不多说,详细可参考:[工程描述api文档](https://github.com/waruqi/xmake/wiki/%E5%B7%A5%E7%A8%8B%E6%8F%8F%E8%BF%B0api%E6%96%87%E6%A1%A3) 还有些辅助api,例如: * dirs:扫描获取当前指定路径中的所有目录 * files:扫描获取当前指定路径中的所有文件 * format: 格式化字符串,string.format的简写版本 还有变量定义、逻辑操作也是可以使用的,毕竟是基于lua的,该有的基础语法,还是要有的,我们可以通过if来切换编译文件: ```lua target("test") set_kind("static") if is_plat("iphoneos") then add_files("src/test/ios/*.c") else add_files("src/test/*.c") end ``` 我们也可以启用和禁用某个子工程target: ```lua if is_arch("arm*") then target("test1") set_kind("static") add_files("src/*.c") else target("test2") set_kind("static") add_files("src/*.c") end ``` 需要注意的是,变量定义分全局变量和局部变量,局部变量只对当前xmake.lua有效,不影响子xmake.lua ```lua -- 局部变量,只对当前xmake.lua有效 local var1 = 0 -- 全局变量,影响所有之后 add_subfiles(), add_subdirs() 包含的子 xmake.lua var2 = 1 add_subdirs("src") ``` #### 内部作用域 也称插件、脚本作用域,提供更加复杂、灵活的脚本支持,一般用于编写一些自定义脚本、插件开发、自定义task任务、自定义模块等等 一般通过 `function () end` 包含,并且被传入到 `on_xxx`, `before_xxx`和`after_xxx`接口内的,都属于自作用域。 例如: ```lua -- 自定义脚本 target("hello") after_build(function () -- 内部作用域 end) -- 自定义任务、插件 task("hello") on_run(function () -- 内部作用域 end) ``` 在此作用域中,不仅可以使用大部分lua的api,还可以使用很多xmake提供的扩展模块,所有扩展模块,通过`import`来导入 具体可参考:[插件开发之import类库](https://xmake.io/zh/) 这里我们给个简单的例子,在编译完成后,对ios目标程序进行ldid签名: ```lua target("iosdemo") set_kind("binary") add_files("*.m") after_build( function (target) -- 执行签名,如果失败,自动中断,给出高亮错误信息 os.run("ldid -S$(projectdir)/entitlements.plist %s", target:targetfile()) end) ``` 需要注意的是,在内部作用域中,所有的调用都是启用异常捕获机制的,如果运行出错,会自动中断xmake,并给出错误提示信息 因此,脚本写起来,不需要繁琐的`if retval then` 判断,脚本逻辑更加一目了然 #### 接口作用域 在外部作用域中的所有描述api设置,本身也是有作用域之分的,在不同地方调用,影响范围也不相同,例如: ```lua -- 全局根作用域,影响所有target,包括 add_subdirs() 中的子工程target设置 add_defines("DEBUG") -- 定义或者进入demo目标作用域(支持多次进入来追加设置) target("demo") set_kind("shared") add_files("src/*.c") -- 当前target作用域,仅仅影响当前target add_defines("DEBUG2") -- 选项设置,仅支持局部设置,不受全局api设置所影响 option("test") -- 当前选项的局部作用域 set_default(false) -- 其他target设置,-DDEBUG 也会被设置上 target("demo2") set_kind("binary") add_files("src/*.c") -- 重新进入demo目标作用域 target("demo") -- 追加宏定义,只对当前demo目标有效 add_defines("DEBUG3") ``` xmake里面还有些全局api,仅提供全局作用域支持,例如: * add\_subfiles() * add\_subdirs() * add\_packagedirs() 等等,这些调用不要放置在 target 或者 option 的局部作用域之间,虽然没什么实际区别,但是会影响可读性,容易被误导 使用方式,如下: ```lua target("xxxx") set_kind("binary") add_files("*.c") -- 包含子模块文件 add_subdirs("src") ``` #### 作用域缩进 xmake.lua里面缩进,只是个编写规范,用于更加清楚的区分,当前的设置 是针对 那个作用域的,虽然就算不缩进,也一样ok,但是可读性上 并不是很好。。 例如: ```lua target("xxxx") set_kind("binary") add_files("*.c") ``` 和 ```lua target("xxxx") set_kind("binary") add_files("*.c") ``` 上述两种方式,效果上都是一样的,但是理解上,第一种更加直观,一看就知道 add\_files 仅仅只是针对 target 设置的,并不是全局设置 因此,适当的进行缩进,有助于更好的维护xmake.lua 最后附上,[tbox](https://github.com/waruqi/tbox)的[xmake.lua](https://github.com/waruqi/tbox/blob/master/xmake.lua)和[src/tbox/xmake.lua](https://github.com/waruqi/tbox/blob/master/src/tbox/xmake.lua)描述,仅供参考。。 --- --- url: /zh/posts/app-to-ipa.md --- 最近在做ios app的企业测试包,需要频繁打包分发给测试,因此将编译完的.app打包成ipa单独分发出去,这里调研下几种打包方案: 1. 直接通过iTunes来打包 2. 调用zip写个打包脚本 3. 使用第三方脚本和工具 为了方便日常ios app打包程ipa,觉得可以把这个脚本放到xmake中去,作为一个小插件提供,也是个不错的方式。 因此顺手在xmake里面加了这么一个ipa to app的小插件,进行快速打包,使用方式如下: ```bash $ xmake app2ipa --icon=Icon.png /xxx/xxx.app ``` icon参数指定的是app的主图标,用作iTunesArtwork,目前还不能自动设置,需要手动指定哦。。 后面只需要传入需要打包的xxx.app的路径就可以了,默认ipa会载同目录下生成/xxx/xxx.ipa,也可以通过`--ipa/-o`指定输出路径。 注:这只是个小工具,目前还不支持自动修改签名,有兴趣的同学,可以提pr上来,加上这个功能哦。 --- --- url: /zh/posts/xmake-sourcecode-arch.md --- title: xmake 源码架构剖析 tags: \[xmake, lua, 源码, 架构设计] date: 2017-09-28 author: Ruki *** 本文主要介绍下[xmake](https://github.com/xmake-io/xmake)的整体架构设计,以及源码结构的布局和模块划分。 如果你想深度使用xmake,开发xmake插件、工程自定义脚本或者想为xmake贡献一些代码和特性,可以通过此本的介绍,对xmake项目整体有个大概的了解。, 源码地址:[Github](https://github.com/xmake-io/xmake) ## 顶层目录结构 ```bash ./xmake/ ├── actions # 内建的一些基础task,用于基本的构建安装等操作 ├── core # xmake的核心模块,提供最底层的实现支持 ├── languages # 所有的语言相关特性支持和扩展,都在此目录下 ├── modules # 内置的扩展模块,可用`import`导入使用 ├── packages # 内置包目录,提供xmake所需的一些必须依赖包支持,例如:git等,其他第三方包放在独立xmake-repo下 ├── platforms # 平台支持目录,提供各个构建平台的配置信息和脚本 ├── plugins # 插件目录,提供一些内置的插件支持,例如:生成IDE工程,宏脚本等。。 ├── scripts # 放置一些杂七杂八的shell、perl等其他语言脚本,例如:gas-preprocessor.pl └── templates # 工程模板目录,提供一些`xmake create`创建工程所需的内置模板 ``` ## 沙盒模式 为了简化`xmake.lua`中自己定义脚本、插件脚本以及扩展模块的开发,提供更高的安全性,xmake采用了沙盒的方式去加载他们,每个脚本都独立在一个单独的沙盒中,并且有独立的作用域支持。 在xmake的整个目录结构中,除了`xmake/core`底层核心模块,其他的顶层目录全部放置在沙盒中加载运行,来简化代码开发,提高易用性和安全性。 这样有什么好处呢? * 上层模块的开发跟xmake的core代码完全隔离,不会干扰到xmake的底层核心模块,更加安全 * 作用域只提供当前模块所需api的最小子集支持 * 提供内置异常处理机制,以及api调用的返回值、传参有效性检测,代码出错后,会及时报出异常信息,并提供详细栈来定位问题 * 可通过`import("core.base.task")`的模块导入方式,提供更加方便高效的模块加载支持 * 通过异常机制,简化代码的编写,可以简化30%的代码量,以及错误判断 * 常用接口字符串参数,支持`$(val)`等内置变量的自动转换 下面我们可以直观感受下`原生代码`和`沙盒代码`的区别: #### 导入和返回值判断的改进 原生代码: ```lua local test = require("modules/test") local ok, errors = test.run("arg1", "arg2") if not ok then os.raise(errors) end ``` 沙盒代码: ```lua import("modules.test") test.run("arg1", "arg2") ``` 如果`test.run`运行出错,会自动中断,并显示出错信息。 import的导入比lua原生的require更加的强大易用,支持很多高级特性: * 多级多目录导入支持,模块的搜索目录可以有多个,也可以在`xmake.lua`中自己指定 * 通过指定父目录,批量加载所有模块,例如:`import("core")`会加载core目录下的所有模块,并且通过`core.xxx`来访问 * 支持模块继承导入 * 支持匿名导入和缓存优化 * 支持别名设置 * 通过下划线区分,仅导入公有接口 * 自动识别main函数,可直接调用,例如:`import("test")(args)`,会自动调用test模块中的main函数 * 支持`xmake lua`直接加载测试,例如:`xmake l lib.detect.find_package zlib` #### 内置api的改进 原生代码: ```lua print("hello xmake") print("hello", "xmake") ``` 沙盒代码: ```lua print("hello xmake") print("hello", "xmake") print("hello %s", "xmake") print("hello $(var)") ``` #### 空参数的判断改进 原生代码: ```lua function test(array) if array ~= nil then for _, item in ipairs(array) do -- ... end end end ``` 沙盒代码: ```lua function test(array) for _, item in ipairs(array) do -- ... end end ``` #### 简化模块定义 原生代码: ```lua local module = module or {} function module.test(arg) -- ... end return module ``` 沙盒代码: ```lua function test(arg) -- ... end ``` ## Actions目录 这个目录下提供xmake日常所需的最基础命令,提供配置、编译、打包、安装、运行、调试、卸载等功能。 ```bash ./xmake/actions/ ├── build # 构建工程 ├── clean # 清理构建产生的文件 ├── config # 构建前的工程配置 ├── create # 根据模板创建工程 ├── global # 全局配置 ├── install # 安装构建好的目标文件到系统 ├── package # 打包当前平台下的构建文件 ├── require # 获取依赖包 ├── run # 运行、调试目标程序 └── uninstall # 卸载安装到系统的目标文件 ``` ## Modules目录 这个是扩展模块目录,提供了一些常用的模块,来扩展`xmake.lua`的自定义脚本,提供更多高级特性,例如:编译器特性检测、依赖包检测等。 ```bash ./xmake/modules/ ├── core │   └── tools # 这个下面的模块,主要用于扩展xmake的编译工具链 │   ├── ar.lua │   ├── cl.lua │   ├── clang.lua │   ├── clangxx.lua │   ├── dmd.lua │   ├── gcc.lua │   ├── gccgo.lua │   ├── gdc.lua │   ├── go.lua │   ├── gxx.lua │   ├── ldc.lua │   ├── lib.lua │   ├── link.lua │   ├── ml.lua │   ├── ml64.lua │   ├── rc.lua │   ├── rustc.lua │   └── swiftc.lua ├── detect │   ├── packages # 用于增强find_package接口的探测 │   │   ├── find_mbedtls.lua │   │   ├── find_mysql.lua │   │   ├── find_openssl.lua │   │   ├── find_pcre.lua │   │   ├── find_pcre2.lua │   │   └── find_zlib.lua │   ├── sdks # 用于查找一些编译sdk环境 │   │   ├── find_cross_toolchains.lua │   │   ├── find_ndk_sdkvers.lua │   │   ├── find_ndk_toolchains.lua │   │   ├── find_vstudio.lua │   │   ├── find_xcode_dir.lua │   │   └── find_xcode_sdkvers.lua │   └── tools # 用于增强可执行工具的查找、特性检测 │   ├── find_7z.lua │   ├── find_apt.lua │   ├── find_ar.lua │   ├── find_brew.lua │   ├── find_ccache.lua │   ├── find_cl.lua │   ├── find_clang.lua │   ├── find_clangxx.lua │   ├── find_curl.lua │   ├── find_dmd.lua │   ├── find_doxygen.lua │   ├── find_gcc.lua │   ├── find_gccgo.lua │   ├── find_gdb.lua │   ├── find_gdc.lua │   ├── find_git.lua │   ├── find_go.lua │   ├── find_gxx.lua │   ├── find_gzip.lua │   ├── find_ldc2.lua │   ├── find_lib.lua │   ├── find_link.lua │   ├── find_lipo.lua │   ├── find_lldb.lua │   ├── find_ml.lua │   ├── find_ml64.lua │   ├── find_ollydbg.lua │   ├── find_pacman.lua │   ├── find_ping.lua │   ├── find_pkg_config.lua │   ├── find_rc.lua │   ├── find_rustc.lua │   ├── find_sudo.lua │   ├── find_swiftc.lua │   ├── find_tar.lua │   ├── find_unzip.lua │   ├── find_vsjitdebugger.lua │   ├── find_wget.lua │   ├── find_windbg.lua │   ├── find_x64dbg.lua │   ├── find_yum.lua │   ├── find_zip.lua ├── devel │   ├── debugger # 调试器支持 │   │   └── run.lua │   └── git # git模块的扩展封装 │   ├── branches.lua │   ├── checkout.lua │   ├── checkurl.lua │   ├── clean.lua │   ├── clone.lua │   ├── ls_remote.lua │   ├── pull.lua │   ├── refs.lua │   └── tags.lua ├── lib │   └── detect # 这个模块,比较实用,用于各种编译器特性探测、语言类型和函数检测 │   ├── check_cxsnippets.lua │   ├── features.lua │   ├── find_tool.lua │   ├── find_toolname.lua │   ├── has_cfuncs.lua │   ├── has_cincludes.lua │   ├── has_ctypes.lua │   ├── has_cxxfuncs.lua │   ├── has_cxxincludes.lua │   ├── has_cxxtypes.lua │   ├── has_features.lua │   ├── has_flags.lua │   └── pkg_config.lua ├── net # 网络模块 │   ├── fasturl.lua │   ├── http │   │   └── download.lua # http下载模块,自动检测curl/wget并调用 │   └── ping.lua ├── package │   └── manager # 系统第三方包管理工具的封装,提供一致性包安装 │   ├── apt │   │   └── install.lua │   ├── brew │   │   └── install.lua │   ├── install.lua │   ├── pacman │   │   └── install.lua │   └── yum │   └── install.lua ├── privilege # 权限管理 │   └── sudo.lua └── utils └── archive # 归档文件的压缩和解压,支持系统常用归档格式:zip/7z/gz/tar/bz2等,自动检测和适配解压工具 └── extract.lua ``` ## Plugins目录 放置内置插件的目录,里面内置了一些常用插件,我们也可以自己扩展插件,或者从[xmake-plugins](https://github.com/xmake-io/xmake-plugins)上面下载一些扩展插件。 ```bash ./xmake/plugins/ ├── doxygen # doxygen文档生成插件 ├── hello # xmake插件的一个demo ├── lua # 加载和测试lua脚本、xmake的模块,例如:xmake l lib.detect.find_tool git │   └── scripts ├── macro # 宏记录插件,记录和回放所有执行过的xmake命令,一般用于批量构建和打包 │   └── macros ├── project # IDE工程文件生成插件,支持:vs200x, vs201x, makefile等工程文件 │   ├── clang │   ├── makefile │   └── vstudio └── repo # 依赖包仓库管理 ``` ## Platforms目录 提供一些构建平台的配置和脚本处理,也可自行扩展自己的平台。 ```bash ./xmake/platforms/ ├── android # 基于android ndk的编译 ├── cross # 主要用于交叉编译 ├── iphoneos # ios平台的编译,支持模拟器架构 ├── linux # linux平台,也支持linux环境的交叉编译工具链 ├── macosx # macosx的环境编译 ├── mingw # 基于mingw工具链的编译,支持windows/macosx/linux下的mingw ├── watchos # apple watch 平台的编译 └── windows # windows平台的编译,可直接在cmd终端下进行,不需要cygwin/msys支持 ``` ## Languages目录 这个目录提供xmake编译指定的代码语言所需的一些配置信息和脚本处理,我们可以自己扩展这个目录,来提供其他语言编译的支持。 ```bash ./xmake/languages/ ├── asm ├── c++ ├── dlang ├── golang ├── msrc ├── objc++ ├── rust └── swift ``` ## Templates目录 这个目录主要提供`xmake create`创建空工程所需的一些内置工程模板。 ```bash ./xmake/templates/ ├── c │   ├── console │   ├── console_tbox │   ├── shared_library │   ├── shared_library_tbox │   ├── static_library │   └── static_library_tbox ├── c++ │   ├── console │   ├── console_tbox │   ├── shared_library │   ├── shared_library_tbox │   ├── static_library │   └── static_library_tbox ├── dlang │   ├── console │   ├── shared_library │   └── static_library ├── go │   ├── console │   └── static_library ├── objc │   └── console ├── objc++ │   └── console ├── rust │   ├── console │   └── static_library └── swift └── console ``` ## Core目录 core比较复杂,它是xmake最底层的支撑,提供了沙盒机制、解释器、工程处理、基础模块、插件加载的核心实现,里面的所有模块都不在沙盒里面,所以跟其他目录里面的模块是完全隔离的。 ```bash ./xmake/core/ ├── _xmake_main.lua # xmake的脚本起始入口 ├── base # 基础模块 │   ├── colors.lua # ${red}等色彩输出的基础支持 │   ├── coroutine.lua # 协程封装 │   ├── deprecated.lua │   ├── emoji.lua │   ├── filter.lua # $(val)变量的处理器 │   ├── global.lua │   ├── interpreter.lua # xmake.lua的解释器 │   ├── io.lua │   ├── option.lua # 命令行输入参数的解析和获取 │   ├── os.lua │   ├── path.lua │   ├── privilege.lua │   ├── process.lua │   ├── profiler.lua # 性能分析器 │   ├── string.lua │   ├── table.lua │   ├── task.lua # task任务、插件处理模块 │   └── utils.lua ├── language # 代码语言模块,会去加载languages目录下的指定语言配置 │   ├── language.lua │   └── menu.lua ├── main.lua # xmake的主入口 ├── package # 包依赖支持 │   ├── package.lua │   └── repository.lua ├── platform # 平台管理 │   ├── environment.lua │   ├── menu.lua │   └── platform.lua ├── project # 工程管理相关的一些模块 │   ├── cache.lua # 工程缓存维护 │   ├── config.lua # 工程配置文件维护 │   ├── history.lua │   ├── option.lua # option对象的封装 │   ├── project.lua # 工程xmake.lua加载和解析 │   ├── target.lua # target对象的封装 │   └── template.lua # 工程模板的加载 ├── sandbox # 沙盒模块 │   ├── modules # 这里面也提供了一些内置沙盒模块,跟modules目录下的模块的区别就是,这里的模块实现代码本身不基于沙盒,纯原生底层代码实现,可直接调用底层接口 │   │   ├── _g.lua # 这里的模块不需要import,可直接在沙盒脚本中使用 │   │   ├── assert.lua │   │   ├── catch.lua │   │   ├── coroutine.lua │   │   ├── cprint.lua │   │   ├── cprintf.lua │   │   ├── debug.lua │   │   ├── finally.lua │   │   ├── format.lua │   │   ├── hash.lua │   │   ├── ifelse.lua │   │   ├── import # 这里面也提供了一些import所需的内置沙盒模块,里面的实现代码,可直接调用core里面底层接口,并且做了异常捕获和返回值检测 │   │   │   ├── core │   │   │   │   ├── base │   │   │   │   │   ├── colors.lua │   │   │   │   │   ├── filter.lua │   │   │   │   │   ├── global.lua │   │   │   │   │   ├── option.lua │   │   │   │   │   ├── privilege.lua │   │   │   │   │   ├── semver.lua │   │   │   │   │   └── task.lua │   │   │   │   ├── language │   │   │   │   │   ├── language.lua │   │   │   │   │   └── menu.lua │   │   │   │   ├── package │   │   │   │   │   ├── package.lua │   │   │   │   │   └── repository.lua │   │   │   │   ├── platform │   │   │   │   │   ├── environment.lua │   │   │   │   │   ├── menu.lua │   │   │   │   │   └── platform.lua │   │   │   │   ├── project │   │   │   │   │   ├── cache.lua │   │   │   │   │   ├── config.lua │   │   │   │   │   ├── history.lua │   │   │   │   │   ├── menu.lua │   │   │   │   │   ├── project.lua │   │   │   │   │   ├── target.lua │   │   │   │   │   ├── task.lua │   │   │   │   │   └── template.lua │   │   │   │   ├── sandbox │   │   │   │   │   ├── module.lua │   │   │   │   │   └── sandbox.lua │   │   │   │   └── tool │   │   │   │   ├── compiler.lua │   │   │   │   ├── extractor.lua │   │   │   │   └── linker.lua │   │   │   └── lib │   │   │   └── detect # lib.detect.* 下的部分探测接口,跟modules下的那些类似 │   │   │   ├── cache.lua │   │   │   ├── find_directory.lua │   │   │   ├── find_file.lua │   │   │   ├── find_library.lua │   │   │   ├── find_package.lua │   │   │   ├── find_path.lua │   │   │   ├── find_program.lua │   │   │   └── find_programver.lua │   │   ├── import.lua # import接口实现 │   │   ├── inherit.lua │   │   ├── insert.lua │   │   ├── interpreter # xmake.lua上层描述域可直接调用的一些内置模块 │   │   │   ├── format.lua │   │   │   ├── getenv.lua │   │   │   ├── ifelse.lua │   │   │   ├── ipairs.lua │   │   │   ├── os.lua │   │   │   ├── pairs.lua │   │   │   ├── path.lua │   │   │   ├── print.lua │   │   │   ├── printf.lua │   │   │   ├── string.lua │   │   │   ├── table.lua │   │   │   ├── tonumber.lua │   │   │   ├── tostring.lua │   │   │   └── type.lua │   │   ├── io.lua # 这下面是一些不需要import的内置接口,部分接口做了些改进 │   │   ├── ipairs.lua │   │   ├── math.lua │   │   ├── os.lua │   │   ├── pairs.lua │   │   ├── path.lua │   │   ├── print.lua │   │   ├── printf.lua │   │   ├── process.lua │   │   ├── raise.lua │   │   ├── string.lua │   │   ├── table.lua │   │   ├── tonumber.lua │   │   ├── tostring.lua │   │   ├── try.lua │   │   ├── type.lua │   │   ├── unpack.lua │   │   ├── utils.lua │   │   ├── val.lua │   │   ├── vformat.lua │   │   ├── vprint.lua │   │   └── vprintf.lua │   └── sandbox.lua └── tool # 编译器、链接器等相关工具的封装模块,可通过`import("core.tool.compiler")`来使用 ├── builder.lua ├── compiler.lua ├── extractor.lua ├── linker.lua └── tool.lua ``` --- --- url: /zh/posts/custom-rule.md --- 在2.1.9版本之后,xmake不仅原生内置支持多种语言文件的构建,而且还可以通过自定义构建规则,让用户自己来实现复杂的未知文件构建。 具体使用介绍,可参考相关文档:[rule规则使用手册](https://xmake.io/zh/) #### 通用规则 我们可以通过预先设置规则支持的文件后缀,来扩展其他文件的构建支持: ```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") ``` 我们也可以指定某些零散的其他文件作为markdown规则来处理: ```lua target("test") -- ... add_files("src/test/*.md.in", {rule = "markdown"}) ``` 注:通过`add_files("*.md", {rule = "markdown"})`方式指定的规则,优先级高于`add_rules("markdown")`设置的规则。 #### 依赖构建 我们还可以实现规则的级联构建,例如在构建man规则后,继续调用markdown规则,实现级联构建: ```lua rule("man") add_imports("core.project.rule") on_build(function (target, sourcefile) rule.build("markdown", target, sourcefile) end) ``` 其中`add_imports`用于预先导入扩展模块,可在多个自定义脚本中直接使用,具体说明见:[add\_imports文档](https://xmake.io/zh/) #### 多文件构建 对于有些文件,需要支持多文件构建生成单一对象的模式,可以通过`on_build_all`来实现: ```lua rule("man") on_build_all(function (target, sourcefiles) -- build some source files for _, sourcefile in ipairs(sourcefiles) do -- ... end end) target("test") -- ... add_files("src/test/*.doc.in", {rule = "man"}) ``` #### 清理和安装 我们可以通过`on_clean`, `on_install`用于实现自定义规则的清理和安装逻辑,每次处理一个源文件,例如: ```lua rule("markdown") on_build(function (target, sourcefile) os.cp(sourcefile, path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) end) on_clean(function (target, sourcefile) os.rm(path.join(target:targetdir(), path.basename(sourcefile) .. ".html")) end) on_install(function (target, sourcefile) import("core.base.option") os.cp(path.join(target:targetdir(), path.basename(sourcefile) .. ".html"), option.get("outputdir")) end) ``` 最后附上一个完整例子,请供参考:[rule使用例子](https://github.com/xmake-io/xmake/issues/149) --- --- url: /zh/posts/xmake-vscode.md --- title: xmake-vscode插件开发过程记录 tags: \[xmake, vscode, 插件开发] date: 2017-10-11 author: Ruki *** 最近打算给[xmake](https://github.com/xmake-io/xmake)写一些IDE和编辑器的集成插件,发现vscode的编辑器插件比较容易上手的,就先研究了下vscode的插件开发流程,并且完成了[xmake-vscode](https://github.com/xmake-io/xmake-vscode)插件的开发。 我们先来看几张最后的效果图: ## 语法高亮和自动补全 ## 状态栏 ![statusbar](/assets/img/posts/xmake/xmake-vscode-statusbar.png) 要实现上面的效果,其实并不复杂,首先我们先来简单介绍下,vscode的插件开发的基本流程: ## 安装插件开发环境 #### 安装cnpm 由于国内环境比较复杂,直接用npm安装也许很慢或者访问不稳定,因此这里先安装了cnpm去默认使用淘宝的镜像源。 ```console $ npm install -g cnpm --registry=https://registry.npm.taobao.org ``` #### 创建空工程 通过cnpm去安装yo工具,用来创建一个vscode插件的空工程 ```console $ cnpm install -g yo generator-code $ yo code ``` 大体的源码结构如下: 选择创建项目后有四个输入和一个选择: * 输入你扩展的名称 xmake-vscode * 输入一个标志(项目创建的文件名称用这个)xmake-vscode * 输入对这个扩展的描述 * 输入以后要发布用到的一名称(和以后再发布时候有一个名字是对应上的)tboox * 是问你要不要创建一个git仓库用于版本管理 创建完成后的空工程,我们可以用vscode直接打开,然后进行调试加载运行下: 加载起来后,敲F1打开命令窗口,运行默认的hello world测试命令: 到此,一个简答的demo插件就搞定了,接下来我们简单介绍下如何发布这个插件到vscode的market上去。 #### 创建发布者 首先我们需要在[marketplace.visualstudio.com](https://xmake.io/zh/)上注册一个账号,创建一个发布者,这里我取名为tboox 然后,我们需要在自己的账号里面,添加一个Personal Access Token(地址:`https://[your name].visualstudio.com/_details/security/tokens`,注意Token只显示一次,最好自己保存一份) 接着,我们安装下vsce这个工具,用于vscode的插件工程打包编译和发布。 ```console $ cnpm install -g vsce ``` 安装好vsce后,我们先创建一个发布者,这里为tboox,输入刚刚market账号里面提供的token进行绑定。 ```console $ vsce create-publisher (publisher name) ``` #### 构建发布 最后,只需要通过下面命令进行打包或者发布就行了,如果仅仅打个本地包,拖入vscode加载测试,可以运行: ```console $ vsce package ``` 这将会生成一个类似`xmake-vscode-0.0.1.vslx`的插件包文件,用vscode可直接加载运行。 如果,我们已经开发完了插件,想要发布到market市场,可以执行: ```console $ vsce publish [version] ``` 这个时候,我们就可以在[xmake-vscode on marketplace](https://xmake.io/zh/)上看到你的插件了,用户也可以直接通过vscode进行搜索和安装使用。 ## 插件开发详解 #### 插件的加载机制 插件通过工程根目录extension.json中配置的activationEvents进行触发,例如: ```json { "activationEvents": [ "workspaceContains:xmake.lua", "onCommand:xmake.sayHello" ] } ``` 当vscode打开带有`xmake.lua`的目录或者执行`xmake.XXX`相关命令的时候,都会触发加载xmake-vscode插件,然后调用`src/extension.ts`中的activate入口函数,进行插件的加载和初始化。 ```js export function activate(context: vscode.ExtensionContext) { let disposable = vscode.commands.registerCommand('xmake.sayHello', () => { vscode.window.showInformationMessage('Hello XMake!'); }); context.subscriptions.push(disposable); } ``` 上述代码,在加载插件的时候,注册`sayHello`命令,去显示`Hello XMake!`提示信息。 #### 创建自定义输出 vscode通过创建OutputChannel来输出自己的日志信息,代码如下: ```js import * as vscode from 'vscode'; let log = vscode.window.createOutputChannel("xmake/log"); log.show(); log.appendLine("hello xmake!"); ``` 在创建的时候可以指定一个label名,用于区分不同的输出通道,最后显示的结果如下: 需要注意的是,必须执行`log.show()`,输出才会被显示出来,并且输出行为是带缓存刷新的,并不会实时输出,也不支持色彩高亮输出。 #### 创建和控制终端 之前,xmake-vscode就是采用channel的方式来输出xmake的构建信息,效果不是很理想,因此后来改用了终端直接执行的方式,可以看下下面的效果图: 那如何控制终端,执行自己的命令呢,其实也非常简单: ```js let terminal = vscode.window.createTerminal({name: "xmake"}); terminal.show(true); terminal.sendText("xmake"); ``` 上面的代码,通过创建一个label名为xmake的独立终端,然后发送执行命令:`xmake`,去让终端执行xmake进行项目的构建,当然如果要显示出来,还是要先调用下`terminal.show(true)`。 #### 添加和读取全局配置 xmake-vscode里面增加了一些全局vscode配置项,用于控制xmake-vscode插件的行为,配置清单是在package.json文件中进行描述的,例如: ```json { "configuration": { "type": "object", "title": "XMake configuration", "properties": { "xmake.logLevel": { "type": "string", "default": "normal", "description": "The Log Level: normal/verbose/minimal", "enum": [ "verbose", "normal", "minimal" ] }, "xmake.buildDirectory": { "type": "string", "default": "${workspaceRoot}/build", "description": "The Build Output Directory" }, "xmake.androidNDKDirectory": { "type": "string", "default": "", "description": "The Android NDK Directory" } } } } ``` 上述配置,增加了三个配置项,都在`xmake.`域下面,可在vscode配置中直接搜索xmake相关字样就能方便找到。 读取配置也很方便,只要获取xmake相关域配置,进行读取就行了: ```js const config = vscode.workspace.getConfiguration('xmake'); config.get("buildDirectory"); ``` #### 创建状态栏 状态栏上的按钮是可以响应之前创建的那些命令的,例如:`xmake.sayHello`等,下面我们在状态栏上创建一个debug按钮,用来调试运行xmake构建的程序: ```js let debugButton = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 4.5); debugButton.command = 'xmake.onDebug'; debugButton.text = `$(bug)`; debugButton.tooltip = "Debug the given target"; debugButton.show(); ``` createStatusBarItem中第二个参数4.5用于控制按钮在状态栏上的布局顺序,创建好后,再设置下一些基础属性就行了,这里按钮的文本直接通过`$(bug)`设置了一个图标来显示,更加的直观。 更多vscode内置支持的图标,可以自己从[octicons](https://octicons.github.com/)上面去找。 点击这个按钮,将会触发`xmake.onDebug`命令,然后在终端上执行`xmake run -d`命令,去运行调试程序。 #### 添加选项输入列表 在[xmake-vscode](https://github.com/xmake-io/xmake-vscode)的状态栏上,我们还增加了几个快速配置的状态按钮,用于快速切换不同的平台、架构、编译模式,例如: 这个时候,需要有个选项选择列表的支持,在点击按钮后,列出可以选择的几个选项,然后选择切换,那如何创建这个选项列表呢,直接上代码: ```js // 初始化选项列表清单 let items: vscode.QuickPickItem[] = []; items.push({label: "linux", description: "The Linux Platform"}); items.push({label: "macosx", description: "The MacOS Platform"}); items.push({label: "windows", description: "The Windows Platform"}); items.push({label: "android", description: "The Android Platform"}); items.push({label: "iphoneos", description: "The iPhoneOS Platform"}); items.push({label: "watchos", description: "The WatchOS Platform"}); items.push({label: "mingw", description: "The MingW Platform"}); items.push({label: "cross", description: "The Cross Platform"}); // 显示选项列表,提示用户选择 const chosen: vscode.QuickPickItem|undefined = await vscode.window.showQuickPick(items); if (chosen) { // 获取选择后的结果,然后更新状态栏按钮文本 platButton.text = chosen.label; } ``` #### 自定义语法高亮 语法高亮完全可以通过配置文件来搞定,不用写代码,当然也可以在代码中动态配置,这样稍微繁琐些。 xmake-vscode里面需要处理工程xmake.lua描述文件的语法高亮,因此这边在package.json里面先定义了一个叫xmake的语言类型,如果编辑器打开`xmake.lua`文件,就会对其进行语法高亮处理。 ```json { "contributes": { "languages": [ { "id": "xmake", "filenames": [ "xmake.lua" ], "aliases": [ "XMake" ], "configuration": "./languages/xmake-configuration.json" } ], "grammars": [ { "language": "xmake", "scopeName": "source.xmake", "path": "./languages/xmake-grammars.json" } ] } } ``` 跟语法高亮相关的描述,都放置在`/languages/xmake-grammars.json`中,用json来描述,我们也可以用xml的格式来描述,但是这样可读性不是很好。 `xmake-grammars.json`中的描述规则,我们摘录自lua的grammars文件,因为`xmake.lua`本身就是基于lua语法的,例如,我们匹配`'xxx'`单引号字符串的规则,进行字符串的高亮输出。 ```json { "begin": "'", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.xmake" } }, "end": "'", "endCaptures": { "0": { "name": "punctuation.definition.string.end.xmake" } }, "name": "string.quoted.single.xmake", "patterns": [ { "include": "#escaped_char" } ] } ``` #### 自动补全的实现 代码的自动提示和补全比较麻烦下,需要写个自定义的class,通过languages进行注册: ```js vscode.languages.registerCompletionItemProvider("xmake", new Completion()); ``` 这里我们定义了一个Completion类,注册到xmake语言上去,xmake语言定义,就是刚才讲的在package.json中的配置。 然后我们实现下这个Completion类: ```js export class Completion implements vscode.CompletionItemProvider { // 匹配当前输入,提供需要补全的候选文本列表 public provideCompletionItems(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { // 获取当前输入的单词文本 let wordAtPosition = document.getWordRangeAtPosition(position); var currentWord = ''; if (wordAtPosition && wordAtPosition.start.character < position.character) { var word = document.getText(wordAtPosition); currentWord = word.substr(0, position.character - wordAtPosition.start.character); } // 猜测匹配结果,返回候选列表 return new Promise(function (resolve, reject) { Promise.all([ getLuaKeywordsSuggestions(currentWord), getXMakeCommandsSuggestions(currentWord) ]).then(function (results) { var suggestions = Array.prototype.concat.apply([], results); resolve(suggestions); }).catch(err => { reject(err); }); }); } // 这里可以对刚刚返回的候选文本列表在做二次处理,例如:增加详细的文档描述信息 public resolveCompletionItem(item: vscode.CompletionItem, token: vscode.CancellationToken): Thenable { // 对每个候选文本增加文档描述 return new Promise(function (resolve, reject) { item.documentation = "xxxxxxxxxxx"; resolve(item); }); } } ``` 这部分代码比较多,就不完全贴出来了,完整实现,可参考:[completion.ts](https://github.com/xmake-io/xmake-vscode/blob/master/src/completion.ts) #### 错误和警告输出解析 当编译出错或者出现警告信息的时候,xmake-vscode会自动提取解析编译器的错误信息,然后显示到vscode的问题列表中,并提供代码行的跳转支持。 这里使用的是`vscode.languages.createDiagnosticCollection`来创建显示问题列表: ```js export class ProblemList implements vscode.Disposable { // the diagnostic collection private _diagnosticCollection: vscode.DiagnosticCollection; // the constructor constructor() { // init diagnostic collection this._diagnosticCollection = vscode.languages.createDiagnosticCollection("build"); } // dispose public dispose() { this._diagnosticCollection.dispose(); } // clear problems public clear() { // clear the previous problems first this._diagnosticCollection.clear(); } // diagnose problems from the current logfile public diagnose(logfile: string) { // clear the previous problems first this._diagnosticCollection.clear(); // exists logfile? if (logfile) { const isWin = os.platform() == "win32"; fs.readFile(logfile, isWin? null : "utf8", (err, content) => { if (!err && content) { // 处理msvc输出的gbk编码文件 let text = content; if (isWin) { text = encoding.convert(content, "utf8", "gbk").toString(); } // 用于解析gcc/clang的编译输出 const rOutputGcc: RegExp = new RegExp("^(error: )?(.*?):([0-9]*):([0-9]*): (.*?): (.*)$"); // 用于解析msvc的编译输出 const rOutputMsvc: RegExp = new RegExp("(.*?)\\(([0-9]*)\\): (.*?) .*?: (.*)"); // init diagnostics map let diagnosticsMap: Map = new Map(); // 解析提取每行的错误信息 text.split("\n").forEach(textLine => { if (textLine) { // parse warning and error from the given text line let matches: RegExpExecArray = isWin? rOutputMsvc.exec(textLine) : rOutputGcc.exec(textLine); if (matches) { // 解析提示中代码行和文件位置 let file = ""; let line = "0"; let column = "0"; let kind = "error"; let message = ""; if (isWin) { file = matches[1].trim(); line = matches[2].trim(); kind = matches[3].toLocaleLowerCase().trim(); message = matches[4].trim(); } else { file = matches[2].trim(); line = matches[3].trim(); column = matches[4].trim(); kind = matches[5].toLocaleLowerCase().trim(); message = matches[6].trim(); } // get uri of file let uri: vscode.Uri = vscode.Uri.file(path.isAbsolute(file)? file : path.join(config.workingDirectory, file)); // get diagnostics of this file let diagnostics: vscode.Diagnostic[] = diagnosticsMap.get(uri.fsPath); // 是错误还是警告? let severity: vscode.DiagnosticSeverity = {error: vscode.DiagnosticSeverity.Error, warning: vscode.DiagnosticSeverity.Warning}[kind]; if (severity != vscode.DiagnosticSeverity.Error && severity != vscode.DiagnosticSeverity.Warning) { severity = vscode.DiagnosticSeverity.Error; } // 定位错误行和列位置 let startLine = Number(line); let startColumn = Number(column); if (startLine > 0) startLine -= 1; if (startColumn > 0) startColumn -= 1; // get end line and column let endLine = startLine; let endColumn = startColumn; // init range let range = new vscode.Range(startLine, startColumn, endLine, endColumn); // 添加一个问题描述 let diagnostic = new vscode.Diagnostic(range, message, severity); if (!diagnostics) { diagnostics = []; diagnosticsMap.set(uri.fsPath, diagnostics); } diagnostics.push(diagnostic); } } }); // 更新显示问题列表 diagnosticsMap.forEach((diagnostics: vscode.Diagnostic[], fsPath:string) => { this._diagnosticCollection.set(vscode.Uri.file(fsPath), diagnostics); }); } else if (err) { log.error(err.message); } }); } } } ``` ## 结语 本文讲述的一些vscode插件代码都来自[xmake-vscode](https://github.com/xmake-io/xmake-vscode),有兴趣的同学可以直接参考源码,写个自己的插件。 --- --- url: /zh/posts/project-add-files.md --- 如果你看了[工程描述入门](https://xmake.io/zh/),那么是否觉得通过 add\_files 添加源文件相当的方便? 目前它可以支持`.c/.cpp/.s/.S/.m/.mm/.o/.obj/.a/.lib`这些后缀的源代码和库文件,其中通配符\*表示匹配当前目录下文件,而\*\*则匹配多级目录下的文件。 例如: ```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下的文件,这个是要注意的。。 下面来看个[TBOX](https://github.com/waruqi/tbox)的xmake.lua中`add_files`的例子: ```lua add_files("*.c") add_files("asio/aioo.c") add_files("asio/aiop.c") add_files("math/**.c") -- 这里过滤了libc/string/impl/**.c add_files("libc/**.c|string/impl/**.c") add_files("utils/*.c|option.c") add_files("prefix/**.c") add_files("memory/**.c") add_files("string/**.c") -- 这里过滤了stream/**/charset.c,stream/**/zip.c,stream/**async_**.c,stream/transfer_pool.c add_files("stream/**.c|**/charset.c|**/zip.c|**async_**.c|transfer_pool.c") -- 这里过滤了network/impl/ssl下的所有c文件 add_files("network/**.c|impl/ssl/*.c") add_files("algorithm/**.c") add_files("container/**.c") add_files("libm/libm.c") add_files("libm/idivi8.c") add_files("libm/ilog2i.c") add_files("libm/isqrti.c") add_files("libm/isqrti64.c") add_files("libm/idivi8.c") add_files("platform/*.c|aicp.c") -- 如果当前架构是arm,则添加arm相关的asm优化代码 if is_arch("arm.*") then add_files("utils/impl/crc_arm.S") end -- 如果当前启用了charset模块,那么添加对应的c文件(这里的文件在上面是被过滤掉的) -- options接口后续会详解 if options("charset") then add_files("charset/**.c") add_files("stream/impl/filter/charset.c") end ``` 添加文件的时候支持过滤一些文件的一个好处就是,可以为后续根据不同开关逻辑添加文件提供基础。 尤其需要提一下的是,xmake还支持直接添加`.o/obj/.a/.lib`的对象文件和库文件到目标中 这个跟add\_links是有区别的,links是链接库中的代码,而这个是直接将静态库中的对象文件合并到目标程序中。。 这个的详细介绍,可参看[高级特性之合并静态库](https://xmake.io/zh/) --- --- url: /zh/posts/quickstart-1-installation.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解xmake在各个平台下的安装过程。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ## 安装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 ``` 注:如果ps脚本执行提示失败,可以尝试在管理员模式下执行 ## 安装Windows版本 ### 使用安装包 windows下提供了预制的nsis安装包,我们可直接从github的Releases下载页面下载后,运行安装包即可。 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 ``` 或者下载deb包来安装: 1. 从 [Releases](https://github.com/xmake-io/xmake/releases) 上下载deb安装包 2. 运行: `dpkg -i xmake-xxxx.deb` ## Termux 最新版本的xmake已经很好地支持了termux,而我们也通常只需要执行上面的一键安装脚本即可,如果失败,可参考下文自己拉取源码编译安装。 ## 源码编译安装 ### 安装 注:切记,xmake不建议在root下安装,所以尽量不要在root下拉取源码编译安装! ```bash $ git clone --recursive https://github.com/xmake-io/xmake.git $ cd ./xmake $ ./scripts/get.sh __local__ $ source ~/.xmake/profile ``` 如果觉得github的源太慢,可以通过gitee的镜像源拉取:`clone --recursive https://gitee.com/tboox/xmake.git` 注:由于目前xmake源码通过git submodule维护依赖,所以clone的时候需要加上`--recursive`参数同时拉取所有submodules代码,请不要直接下载tar.gz源码,因为github不会自动打包submodules里面的代码。 如果git clone的时候忘记加`--recursive`,那么也可以执行`git submodule update --init`来拉取所有submodules,例如: ```bash $ git clone https://github.com/xmake-io/xmake.git $ cd ./xmake $ git submodule update --init $ ./scripts/get.sh __local__ ``` 注:`./get.sh __local__`是安装到`~/.local/xmake`下,然后通过`source ~/.xmake/profile`方式来加载的,所以安装完,当前终端如果执行xmake失败,提示找不到,就手动执行下 `source ~/.xmake/profile`,而下次打开终端就不需要了。 ### 卸载 ```bash $ ./scripts/get.sh __uninstall__ ``` ### 仅仅更新安装lua脚本 这个开发者本地调试xmake源码才需要: ```bash $ ./scripts/get.sh __local__ __install_only__ ``` ### root下安装 xmake不推荐root下安装使用,因为这很不安全,如果用户非要root下装,装完后,如果提示xmake运行不了,请根据提示传递`--root`参数,或者设置`XMAKE_ROOT=y`环境变量强行启用下,前提是:用户需要随时注意root下误操作系统文件文件的风险。 ### 依赖问题 1. 如果遇到readline相关问题,请装下readline-devel或者libreadline-dev依赖,这个是可选的,仅仅`xmake lua`命令执行REPL时候才需要。 2. 如果想要提速编译,可以装下ccache,xmake会自动检测并使用,这也是可选的。 ## 其他安装方式 注:这种也是源码编译安装,但是安装路径会直接写入`/usr/`下,需要root权限,因此除非特殊情况,不推荐这种安装方式,建议采用上文提供的`./get.sh __local__`方式来安装,这两种安装方式的安装路径是不同的,不要混用。 通过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 ``` 从指定git源更新 ```bash $ xmake update github:xmake-io/xmake#master $ xmake update gitee:tboox/xmake#dev # gitee镜像 ``` 如果xmake/core没动过,仅仅更新xmake的lua脚本改动,可以加`-s/--scriptonly`快速更新lua脚本 ```bash $ xmake update -s dev ``` 最后,我们如果要卸载xmake,也是支持的:`xmake update --uninstall` --- --- url: /zh/posts/quickstart-10-target-deps.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解下,如果在一个项目中维护和生成多个目标文件的生成,以及它们之间的依赖关系设置。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### target到底是什么? xmake的概念定义里,一个独立的项目工程可能会有多个子工程组织在一起,每个子工程对应只能生成一个唯一的目标文件,例如:可执行程序,静态库或者动态库等。 而这里所说的每个子工程就是xmake里面所说的`target`,字面意思就是`目标子工程`。 因此每个子工程,我们都可以通过新增一个target在xmake.lua里面维护,例如: ```lua target("test1") set_kind("binary") add_files("src/test1/*.c") target("test2") set_kind("binary") add_files("src/test2/*.c") ``` 上面我们就定义了两个独立的子工程目标,编译时候会生成两个互不依赖的可执行文件。 ### 从根域继承全局设置 暂时先不谈target间的依赖问题,如果我们有许多通用设置,每个target下都得设置一遍,那会非常冗余,也不好维护。 因此,我们可以把这些配置移到target域的外面,也就是根作用域中去设置,这样对当前xmake.lua以及所有子xmake.lua中的target都会生效,例如: ```lua add_links("tbox") add_linkdirs("lib") add_includedirs("include") target("test1") set_kind("binary") add_files("src/test1/*.c") target("test2") set_kind("binary") add_files("src/test2/*.c") ``` 比如这两target都需要链接tbox库,放置在外层根域设置,test1和test2都能加上对应links。 ### 目标间的依赖设置 那如果某个target需要用到另外一个tatget生成的静态库,应该怎么配置呢? 一种方式就是通过`add_linkdirs`和`add_links`手动指定对应target最后生成的目录库所在目录,然后把链接加上。 ```lua target("foo") set_kind("static") add_files("foo/*.c") add_defines("FOO") target("test1") set_kind("binary") add_includedirs("foo/inc") add_links("foo") add_linkdirs("$(buildir)") add_files("test1/*.c") add_defines("FOO") target("test2") set_kind("binary") add_includedirs("foo/inc") add_links("foo") add_linkdirs("$(buildir)") add_files("test2/*.c") add_defines("FOO") ``` 上述配置中,test1和test2都会用到libfoo库,并且需要获取到libfoo库的头文件路径,库路径和链接,并且在使用过程中还需要额外设置`-DFOO`宏定义开关才行。 看上去没啥,其实这么写有两个问题: 1. test目标和另外两个库目标之间是有编译顺序依赖的,如果test先编译就会提示链接库找不到 2. 配置太过繁琐不好维护,test1和test2有很多冗余配置 那有没有更加简单可靠的配置方式呢,其实我们只需要`add_deps`来对target间配置上依赖关系即可。 ```lua target("foo") set_kind("static") add_files("*.c") add_defines("FOO", {public = true}) add_includedirs("foo/inc", {public = true}) target("test1") set_kind("binary") add_deps("foo") add_files("test1/*.c") target("test2") set_kind("binary") add_deps("foo") add_files("test2/*.c") ``` 对比下,test1和test2的配置,是不是精简了好多?仅仅通过`add_deps("foo")`就继承了libfoo的所有导出设置:linkdirs, links, includedirs以及defines 其中target自身生成的库默认就会自动导出链接设置,而includedirs和defines通过设置public属性,我们也将它们标记为导出,这样可以被test目标继承到。 并且,现在有了依赖关系,xmake在编译的时候,会自动处理这些target之间的编译顺序,保证不会出现链接的时候,libfoo库还没有生成的问题。 ### 依赖继承的进一步解析 #### 级联依赖继承 根据上文所说,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") ``` 上面的配置中,test依赖library2,然后library2又依赖library1,那么通过`add_deps`仅仅添加library2的依赖,test就可以完整继承整个依赖链上的所有导出设置。 #### 禁用默认的继承行为 那如果我们不想继承依赖target的任何配置,如何操作呢? ```lua add_deps("dep1", "dep2", {inherit = false}) ``` 通过显式设置inherit配置,来告诉xmake,这两个依赖的配置是否需要被继承,如果不设置,默认就是启用继承的。 #### 可继承的导出属性详解 上文,我们还通过 `add_includedirs("inc1", {public = true})`, 设置public为true, 将includedirs的设置公开给其他依赖的子target继承。 目前对于target的编译链接flags相关接口设置,都是支持继承属性的,可以人为控制是否需要导出给其他target来依赖继承,目前支持的属性有: | 属性 | 描述 | | ---- | ---- | | private | 默认设置,作为当前target的私有配置,不会被依赖的其他target所继承 | | public | 公有配置,当前target,依赖的子target都会被设置 | | interface | 接口设置,仅被依赖的子target所继承设置,当前target不参与 | 这个其实参考借鉴了cmake的设计,目前xmake中只要跟target相关的所有编译链接设置接口,都是支持可见性导出的,例如:`add_includedirs`, `add_defines`, `add_cflags`等等。 关于这块的详细信息,可以看下:https://github.com/xmake-io/xmake/issues/368 --- --- url: /zh/posts/quickstart-11-subprojects.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解下,如何通过配置子工程模块,来组织构建一个大规模的工程项目。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 维护简单的项目结构 对于一些轻量型的小工程,通常只需要单个xmake.lua文件就能搞定,大体结构如下: ``` projectdir - xmake.lua - src - test - *.c - demo - *.c ``` 源码下面层级简单,通常只需要在项目根目录维护一个xmake.lua来定义所有target就能完成构建,看上去并不是很复杂,也很清晰。 ```lua -- 在根域设置通用配置,当前所有targets都会生效 add_defines("COMMON") target("test") set_kind("static") add_files("src/test/*.c") add_defines("TEST") target("demo") set_kind("static") add_files("src/demo/*.c") add_defines("DEMO") ``` ### 维护复杂的项目结构 但是对于一些大型项目,通常的组织结构层次很多也很深,需要编译的target目标也可能有十几甚至上百个,这个时候如果还是都在根xmake.lua文件中维护,就有点吃不消了。 这个时候,我们就需要通过在每个子工程模块里面,单独创建xmake.lua来维护他们,然后使用xmake提供的includes接口,将他们按层级关系包含进来,最终变成一个树状结构: ``` projectdir - xmake.lua - src - test - xmake.lua - test1 - xmake.lua - test2 - xmake.lua - test3 - xmake.lua - demo - xmake.lua - demo1 - xmake.lua - demo2 - xmake.lua ... ``` 然后,根xmake.lua会将所有子工程的xmake.lua通过层级includes全部引用进来,那么所有定义在子工程的target配置也会完全引用进来,我们在编译的时候永远不需要单独去切到某个子工程目录下操作,只需要: ```bash $ xmake build test1 $ xmake run test3 $ xmake install demo1 ``` 就可以编译,运行,打包以及安装指定的子工程target,所以除非特殊情况,平常不推荐来回切换目录到子工程下单独编译,非常的繁琐。 ### 根xmake.lua文件配置 通常推荐的做法就是在根xmake.lua中仅仅配置一些对所有target都通用的设置,以及includes对子工程的引用,不放置对targets的定义,例如: ```lua -- define project set_project("tbox") set_xmakever("2.3.2") set_version("1.6.5", {build = "%Y%m%d%H%M"}) -- set common flags set_warnings("all", "error") set_languages("c99") add_cxflags("-Wno-error=deprecated-declarations", "-fno-strict-aliasing", "-Wno-error=expansion-to-defined") add_mxflags("-Wno-error=deprecated-declarations", "-fno-strict-aliasing", "-Wno-error=expansion-to-defined") -- add build modes add_rules("mode.release", "mode.debug") -- includes sub-projects includes("test", "demo") ``` xmake里面所有的设置都是按tree状继承的,根xmake.lua中的root域设置会对所有includes的子xmake.lua里面的targets生效, 但反过来不会,子xmake.lua里面的root域设置仅对它下面的子xmake.lua生效,不会影响到父xmake.lua中定义的targets。 ### 子xmake.lua文件配置 所以,我们可以在每个子工程目录中,单独配置xmake.lua,里面的所有配置不会干扰父xmake.lua,只对它下面的更细粒度的子工程生效,就这样一层层按tree状生效下去。 由于,已经在根xmake.lua配置了大部分通用配置,那么我们可以在test子工程下,专心配置只对test有用的设置,例如对于`projectdir/test/xmake.lua`: ```lua add_defines("TEST") target("test1") set_kind("static") add_files("test1/*.c") add_defines("TEST1") target("test2") set_kind("static") add_files("test2/*.c") add_defines("TEST2") ``` 我们可以在这里定义test的所有target,当然也可以继续分层,在每个test1, test2目录下单独维护xmake.lua,这个看自己项目的规模来决定。 比如: ```lua add_defines("TEST") includes("test1", "test2") ``` test1/xmake.lua ```lua target("test1") set_kind("static") add_files("test1/*.c") add_defines("TEST1") ``` test2/xmake.lua ```lua target("test2") set_kind("static") add_files("test2/*.c") add_defines("TEST2") ``` 而这里面的`add_defines("TEST")`在root域,会对test1/test2两个target都生效,但是对于demo目录的target不生效,因为它们是平级的,没有tree状继承关系。 ### 跨xmake.lua间目标依赖 虽然,`projectdir/test/xmake.lua`和`projectdir/demo/xmake.lua`两个子工程目录是平级关系,配置无法相互干扰,但是targets是可以跨xmake.lua访问的,来实现目标间的依赖。 比如demo需要依赖test静态库,进行链接使用,那么demo下xmake.lua可以这么写: ```lua target("demo1") set_kind("binary") add_files("demo1/*.c") add_deps("test1") ``` 只要通过`add_deps("test1")`关联上对应其他子工程目标作为依赖即可,test1静态库会优先编译,并且demo可执行程序会自动link上它生成的libtest1.a库。 ### 文件路径的层级关系 我们需要记住,所有跟路径相关的配置接口,比如`add_files`, `add_includedirs`等都是相对于当前子工程xmake.lua所在的目录的,所以只要添加的文件不跨模块,那么设置起来只需要考虑当前的相对路径就行了。 ``` projectdir - test - xmake.lua - test1/*.c - test2/*.c ``` 比如,这里添加的源文件路径,都是相对于test子工程目录的,我们不需要去设置绝对路径,这样会简化很多。 ```lua target("test1") add_files("test1/*.c") target("test2") add_files("test2/*.c") ``` 当然,如果我们有特殊需求,非要设置工程其他子模块下的文件路径呢?两种办法,通过`../../`的方式一层层绕出去,另外一种就是使用`$(projectdir)`内置变量,它表示项目全局根目录。 比如在demo子工程下: ```lua target("demo1") set_kind("binary") add_files("demo1/*.c") add_files("../../test/test1/*.c") ``` 或者: ```lua target("demo1") set_kind("binary") add_files("demo1/*.c") add_files("$(projectdir)/test/test1/*.c") ``` ### includes接口使用进阶 #### 错误的使用方式 includes这个接口属于全局接口,不隶属于任何target,所以请不要在target内部调用,下面是错误的用法: ```lua target("test") set_kind("static") includes("test1", "test2") add_files("test/*.c") ``` 正确的用法是: ```lua includes("test1", "test2") target("test") set_kind("static") add_files("test/*.c") ``` 或者: ```lua target("test") set_kind("static") add_files("test/*.c") target_end() -- 在下面调用,需要先显式退出target作用域 includes("test1", "test2") ``` #### 引用目录或文件 另外,includes既可以引用目录,也可以直接引用文件,如果test1目录下存在xmake.lua,那么可以直接`includes("test1")`来引用目录。 如果test1目录下是其他xxxx.lua命令的项目文件,可以通过指定文件来引用:`includes("test1/xxxx.lua")`,效果一样的。 #### 模式匹配进行批量导入 includes还支持通过模式匹配的方式来批量导入多个子工程,比如: ```lua includes("test/*/xmake.lua") ``` 可以导入test目录下,所有test1, test2等子工程目录下的配置,如果是`**`还支持递归多级匹配 ```lua includes("test/**/xmake.lua") ``` 通过模式匹配,我们只需要在test/xmake.lua一处地方进行includes,以后用户在新增其他子工程xmake.lua,就会自动导入进来,非常方便。 #### 注意事项 另外,在使用includes的过程中,需要注意的一点是,它不是c语言的`#include`,因此在当前配置中includes子配置,当前配置是不会有任何影响的,比如: ```lua includes("xxx") target("test") -- ... ``` 上面includes了一些子工程,但是这些子工程的配置是不会干扰当前test目标程序的。 --- --- url: /zh/posts/quickstart-12-custom-scripts.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解下,如何通过添加自定义的脚本,在脚本域实现更加复杂灵活的定制。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 配置分离 xmake.lua采用二八原则实现了描述域、脚本域两层分离式配置。 什么是二八原则呢,简单来说,大部分项目的配置,80%的情况下,都是些基础的常规配置,比如:`add_cxflags`, `add_links`等, 只有剩下不到20%的地方才需要额外做些复杂来满足一些特殊的配置需求。 而这剩余的20%的配置通常比较复杂,如果直接充斥在整个xmake.lua里面,会把整个项目的配置整个很混乱,非常不可读。 因此,xmake通过描述域、脚本域两种不同的配置方式,来隔离80%的简单配置以及20%的复杂配置,使得整个xmake.lua看起来非常的清晰直观,可读性和可维护性都达到最佳。 ### 描述域 对于刚入门的新手用户,或者仅仅是维护一些简单的小项目,通过完全在描述配置就已经完全满足需求了,那什么是描述域呢?它长这样: ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") add_syslinks("pthread") ``` 一眼望去,其实就是个 `set_xxx`/`add_xxx`的配置集,对于新手,完全可以不把它当做lua脚本,仅仅作为普通的,但有一些基础规则的配置文件就行了。 这是不是看着更像配置文件了?其实描述域就是配置文件,类似像json等key/values的配置而已,所以即使完全不会lua的新手,也是能很快上手的。 而且,对于通常的项目,仅通过`set_xxx/add_xxx`去配置各种项目设置,已经完全满足需求了。 这也就是开头说的:80%的情况下,可以用最简单的配置规则去简化项目的配置,提高可读性和可维护性,这样对用户和开发者都会非常的友好,也更加直观。 如果我们要针对不同平台,架构做一些条件判断怎么办?没关系,描述域除了基础配置,也是支持条件判断,以及for循环的: ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") if is_plat("linux", "macosx") then add_links("pthread", "m", "dl") end ``` ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") for _, name in ipairs({"pthread", "m", "dl"}) do add_links(name) end ``` 这是不是看着有点像lua了?虽说,平常可以把它当做普通配置问题,但是xmake毕竟基于lua,所以描述域还是支持lua的基础语言特性的。 :::注意 不过需要注意的是,描述域虽然支持lua的脚本语法,但在描述域尽量不要写太复杂的lua脚本,比如一些耗时的函数调用和for循环 ::: 并且在描述域,主要目的是为了设置配置项,因此xmake并没有完全开放所有的模块接口,很多接口在描述域是被禁止调用的, 即使开放出来的一些可调用接口,也是完全只读的,不耗时的安全接口,比如:`os.getenv()`等读取一些常规的系统信息,用于配置逻辑的控制。 :::注意 另外需要注意一点,xmake.lua是会被多次解析的,用于在不同阶段解析不同的配置域:比如:`option()`, `target()`等域。 ::: 因此,不要想着在xmake.lua的描述域,写复杂的lua脚本,也不要在描述域调用print去显示信息,因为会被执行多遍,记住:会被执行多遍!!! ### 脚本域 限制描述域写复杂的lua,各种lua模块和接口都用不了?怎么办?这个时候就是脚本域出场的时候了。 如果用户已经完全熟悉了xmake的描述域配置,并且感觉有些满足不了项目上的一些特殊配置维护了,那么我们可以在脚本域做更加复杂的配置逻辑: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load(function (target) if is_plat("linux", "macosx") then target:add("links", "pthread", "m", "dl") end end) after_build(function (target) import("core.project.config") local targetfile = target:targetfile() os.cp(targetfile, path.join(config.buildir(), path.filename(targetfile))) print("build %s", targetfile) end) ``` 只要是类似:`on_xxx`, `after_xxx`, `before_xxx`等字样的function body内部的脚本,都属于脚本域。 在脚本域中,用户可以干任何事,xmake提供了import接口可以导入xmake内置的各种lua模块,也可以导入用户提供的lua脚本。 我们可以在脚本域实现你想实现的任意功能,甚至写个独立项目出来都是可以的。 对于一些脚本片段,不是很臃肿的话,像上面这么内置写写就足够了,如果需要实现更加复杂的脚本,不想充斥在一个xmake.lua里面,可以把脚本分离到独立的lua文件中去维护。 例如: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load("modules.test.load") on_install("modules.test.install") ``` 我们可以吧自定义的脚本放置到xmake.lua对应目录下,`modules/test/load.lua`和`modules/test/install.lua`中独立维护。 单独的lua脚本文件以main作为主入口,例如: ```lua -- 我们也可以在此处导入一些内置模块或者自己的扩展模块来使用 import("core.project.config") import("mymodule") function main(target) if is_plat("linux", "macosx") then target:add("links", "pthread", "m", "dl") end end ``` 这些独立的lua脚本里面,我们还可以通过[import](https://xmake.io/zh/)导入各种内置模块和自定义模块进来使用,就跟平常写lua, java没啥区别。 而对于脚本的域的不同阶段,`on_load`主要用于target加载时候,做一些动态化的配置,这里不像描述域,只会执行一遍哦!!! 其他阶段,还有很多,比如:`on/after/before`\_`build/install/package/run`等,我们下面会详细描述。 ### import #### 导入扩展摸块 在讲解各个脚本域之前,我们先来简单介绍下xmake的模块导入和使用方式,xmake采用import来引入其他的扩展模块,以及用户自己定义的模块,它可以在下面一些地方使用: * 自定义脚本([on\_build](https://xmake.io/zh/), [on\_run](https://xmake.io/zh/) ..) * 插件开发 * 模板开发 * 平台扩展 * 自定义任务task 导入机制如下: 1. 优先从当前脚本目录下导入 2. 再从扩展类库中导入 导入的语法规则: 基于`.`的类库路径规则,例如: ```lua import("core.base.option") import("core.base.task") function main() -- 获取参数选项 print(option.get("version")) -- 运行任务和插件 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支持的平台列表了 print(p.plats()) end ``` 2.1.5版本新增两个新属性:`import("xxx.xxx", {try = true, anonymous = true})` try为true,则导入的模块不存在的话,仅仅返回nil,并不会抛异常后中断xmake. anonymous为true,则导入的模块不会引入当前作用域,仅仅在import接口返回导入的对象引用。 ### 测试扩展模块 一种方式我们可以在on\_load等脚本中,直接调用print去打印模块的调用结果信息,来测试和验证。 不过xmake还提供了`xmake lua`插件可以更加灵活方便的测试脚本。 #### 运行指定的脚本文件 比如,我们可以直接指定lua脚本来加载运行,这对于想要快速测试一些接口模块,验证自己的某些思路,都是一个不错的方式。 我们先写个简单的lua脚本: ```lua function main() print("hello xmake!") end ``` 然后直接运行它就行了: ```console $ xmake lua /tmp/test.lua ``` #### 直接调用扩展模块 所有内置模块和扩展模块的接口,我们都可以通过`xmake lua`直接调用,例如: ```console $ xmake lua lib.detect.find_tool gcc ``` 上面的命令,我们直接调用了`import("lib.detect.find_tool")`模块接口来快速执行。 #### 运行交互命令 (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 ``` ### 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属性,所有描述域的`set_`, `add_`配置都可以通过这种方式动态配置。 另外,我们可以调用target的一些接口,获取和设置一些基础信息,比如: | target接口 | 描述 | | ----------------------------------- | ---------------------------------------------------------------- | | target:name() | 获取目标名 | | target:targetfile() | 获取目标文件路径 | | target:targetkind() | 获取目标的构建类型 | | target:get("defines") | 获取目标的宏定义 | | target:get("xxx") | 其他通过 `set_/add_`接口设置的target信息,都可以通过此接口来获取 | | target:add("links", "pthread") | 添加目标设置 | | target:set("links", "pthread", "z") | 覆写目标设置 | | target:deps() | 获取目标的所有依赖目标 | | target:dep("depname") | 获取指定的依赖目标 | | target:opts() | 获取目标的所有关联选项 | | target:opt("optname") | 获取指定的关联选项 | | target:pkgs() | 获取目标的所有关联依赖包 | | target:pkg("pkgname") | 获取指定的关联依赖包 | | target:sourcebatches() | 获取目标的所有源文件列表 | ### 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) end) ``` ### target:on\_build\_files #### 自定义编译脚本, 实现多文件构建 通过此接口,可以用来hook指定target内置的构建过程,替换一批同类型源文件编译过程: ```lua target("test") set_kind("binary") add_files("src/*.c") on_build_files(function (target, sourcebatch, opt) end) ``` 设置此接口后,对应源文件列表中文件,就不会出现在自定义的target.on\_build\_file了,因为这个是包含关系。 其中sourcebatch描述了这批同类型源文件: * `sourcebatch.sourcekind`: 获取这批源文件的类型,比如:cc, as, .. * `sourcebatch.sourcefiles()`: 获取源文件列表 * `sourcebatch.objectfiles()`: 获取对象文件列表 * `sourcebatch.dependfiles()`: 获取对应依赖文件列表,存有源文件中编译依赖信息,例如:xxx.d ### target:on\_clean #### 自定义清理脚本 覆盖target目标的`xmake [c|clean}`的清理操作,实现自定义清理过程。 ```lua target("test") -- 设置自定义清理脚本 on_clean(function (target) -- 仅删掉目标文件 os.rm(target:targetfile()) end) ``` ### target:on\_package #### 自定义打包脚本 覆盖target目标的`xmake [p|package}`的打包操作,实现自定义打包过程,如果你想对指定target打包成自己想要的格式,可以通过这个接口自定义它。 ```lua target("demo") set_kind("shared") add_files("jni/*.c") on_package(function (target) os.exec("./gradlew app:assembleDebug") end) ``` 当然这个例子有点老了,这里只是举例说明下用法而已,现在xmake提供了专门的[xmake-gradle](https://github.com/xmake-io/xmake-gradle)插件,来与gradle更好的集成。 ### 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) ``` ### before\_xxx和after\_xxx 需要注意的是,target:on\_xxx的所有接口都覆盖内部默认实现,通常我们并不需要完全复写,只是额外挂接自己的一些逻辑,那么可以使用`target:before_xxx`和`target:after_xxx`系列脚本就行了。 所有的on\_xxx都有对应的before\_和after\_xx版本,参数也完全一致,例如: ```lua target("test") before_build(function (target) print("") end) ``` ### 内置模块 在自定义脚本中,除了使用import接口导入各种扩展模块使用,xmake还提供了很多基础的内置模块,比如:os,io等基础操作,实现更加跨平台的处理系统接口。 #### os.cp os.cp的行为和shell中的`cp`命令类似,不过更加强大,不仅支持模式匹配(使用的是lua模式匹配),而且还确保目的路径递归目录创建、以及支持xmake的内置变量。 例如: ```lua os.cp("$(scriptdir)/*.h", "$(buildir)/inc") os.cp("$(projectdir)/src/test/**.h", "$(buildir)/inc") ``` 上面的代码将:当前`xmake.lua`目录下的所有头文件、工程源码test目录下的头文件全部复制到`$(buildir)`输出目录中。 其中`$(scriptdir)`, `$(projectdir)` 这些变量是xmake的内置变量,具体详情见:[内置变量](https://xmake.io/mirror/zh-cn/manual/builtin_variables.html)的相关文档。 而`*.h`和`**.h`中的匹配模式,跟[add\_files](https://xmake.io/zh/)中的类似,前者是单级目录匹配,后者是递归多级目录匹配。 上面的复制,会把所有文件全部展开复制到指定目录,丢失源目录层级,如果要按保持原有的目录结构复制,可以设置rootdir参数: ```lua os.cp("src/**.h", "/tmp/", {rootdir = "src"}) ``` 上面的脚本可以按`src`根目录,将src下的所有子文件保持目录结构复制过去。 注:尽量使用`os.cp`接口,而不是`os.run("cp ..")`,这样更能保证平台一致性,实现跨平台构建描述。 #### os.run 此接口会安静运行原生shell命令,用于执行第三方的shell命令,但不会回显输出,仅仅在出错后,高亮输出错误信息。 此接口支持参数格式化、内置变量,例如: ```lua -- 格式化参数传入 os.run("echo hello %s!", "xmake") -- 列举构建目录文件 os.run("ls -l $(buildir)") ``` #### os.execv 此接口相比os.run,在执行过程中还会回显输出,并且参数是通过列表方式传入,更加的灵活。 ```lua os.execv("echo", {"hello", "xmake!"}) ``` 另外,此接口还支持一个可选的参数,用于传递设置:重定向输出,执行环境变量设置,例如: ```lua os.execv("echo", {"hello", "xmake!"}, {stdout = outfile, stderr = errfile, envs = {PATH = "xxx;xx", CFLAGS = "xx", curdir = "/tmp"}} ``` 其中,stdout和stderr参数用于传递重定向输出和错误输出,可以直接传入文件路径,也可以传入io.open打开的文件对象。 另外,如果想在这次执行中临时设置和改写一些环境变量,可以传递envs参数,里面的环境变量设置会替换已有的设置,但是不影响外层的执行环境,只影响当前命令。 我们也可以通过`os.getenvs()`接口获取当前所有的环境变量,然后改写部分后传入envs参数。 另外,还能通过curdir参数设置,在执行过程中修改子进程的工作目录。 其相关类似接口还有,os.runv, os.exec, os.execv, os.iorun, os.iorunv等等,比如os.iorun可以获取运行的输出内容。 这块的具体详情和差异,还有更多os接口,都可以到:[os接口文档](https://xmake.io/zh/) 查看。 #### io.readfile 此接口,从指定路径文件读取所有内容,我们可在不打开文件的情况下,直接读取整个文件的内容,更加的方便,例如: ```lua local data = io.readfile("xxx.txt") ``` #### io.writefile 此接口写入所有内容到指定路径文件,我们可在不打开文件的情况下,直接写入整个文件的内容,更加的方便,例如: ```lua io.writefile("xxx.txt", "all data") ``` #### path.join 此接口实现跨平台地路径拼接操作,将多个路径项进行追加拼接,由于`windows/unix`风格的路径差异,使用api来追加路径更加跨平台,例如: ```lua print(path.join("$(tmpdir)", "dir1", "dir2", "file.txt")) ``` 上述拼接在unix上相当于:`$(tmpdir)/dir1/dir2/file.txt`,而在windows上相当于:`$(tmpdir)\\dir1\\dir2\\file.txt` 更多内置模块详情见:[内置模块文档](https://xmake.io/zh/) --- --- url: /zh/posts/quickstart-2-create-and-build-project.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解如何创建一个基于xmake的工程以及编译操作。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 创建空工程 xmake提供了`xmake create`命令,可以很方便的快速创建基于c/c++, swift, objc等各种语言的空工程项目,比如: ```bash $ xmake create test create test ... [+]: xmake.lua [+]: src/main.cpp [+]: .gitignore create ok! ``` 默认会创建一个c++的hello world工程,根目录下会生成一个xmake.lua用于描述项目的构建规则。 ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") ``` 这是一个非常简单的xmake.lua描述,`target("test")`定义了一个子工程模块test,每个target会生成一个对应的目标文件,此处的binary类型,指定创建一个最基础的可执行文件。 而最上面的`mode.debug`和`mode.release`规则设置,是可选设置,但是通常我们都会建议加上,这样默认就可以生效两种常用的构建模式:debug和release ### 执行编译 通常我们如果只是编译当前主机环境的可执行文件,只需要执行xmake这个命令就可以了: ```bash $ xmake checking for the Xcode directory ... /Applications/Xcode.app checking for the SDK version of Xcode ... 10.15 [ 0%]: ccache compiling.release src/main.cpp [100%]: linking.release test ``` xmake默认会检测当前环境已存在的构建环境,比如笔者当前的xcode环境,然后默认采用release模式编译,如果设置了`mode.release`规则,那么就会生效。 ### 编译模式切换 而如果我们要切到`mode.debug`编译,只需要: ```bash $ xmake f -m debug $ xmake ``` 其中,`xmake f`是`xmake config`命令的简写,用来快速的切换配置,如果上手之后,通常采用简写会更加方便,更多命令的简写,都可执行`xmake --help`查看。 ### 创建其他模板工程 `xmake create`还可以用来创建各种其他类型的工程项目,我们可以敲`xmake create --help`看下: ```bash $ xmake create --help Usage: $xmake create [options] [target] Create a new project. Options: -l LANGUAGE, --language=LANGUAGE The project language (default: c++) - c++ - go - dlang - cuda - rust - swift - objc - c - objc++ -t TEMPLATE, --template=TEMPLATE Select the project template id or name of the given language. (default: console) - console: c++, go, dlang, cuda, rust, swift, objc, c, objc++ - qt.console: c++ - qt.quickapp: c++ - qt.quickapp_static: c++ - qt.shared: c++ - qt.static: c++ - qt.widgetapp: c++ - qt.widgetapp_static: c++ - shared: c++, dlang, cuda, c - static: c++, go, dlang, cuda, rust, c - tbox.console: c++, c - tbox.shared: c++, c - tbox.static: c++, c target Create the given target. Uses the project name as target if not exists. ``` 从上面的帮助菜单,我们可以大概了解到,可以通过`-l/--language`来指定工程语言,而`-t/--template`用来指定闯将的工程模板类型。 比如,我们创建一个基于c的静态库项目: ```bash $ xmake create -l c -t static test create test ... [+]: xmake.lua [+]: src/interface.c [+]: src/interface.h [+]: src/test.c [+]: src/main.cpp [+]: .gitignore create ok! ``` 我们也可以创建基于qt的quickapp项目: ```bash $ xmake create -l c++ -t qt.quickapp test create test ... [+]: xmake.lua [+]: src/interface.c [+]: src/main.qml [+]: src/interface.h [+]: src/test.c [+]: src/main.cpp [+]: src/qml.qrc [+]: .gitignore create ok! ``` 除了c/c++项目,xmake还支持其他语言的项目编译,但xmake重点还是在c/c++上,支持其他语言也主要是为了支持跟c/c++进行混合编译,毕竟其他语言向rust什么的官方有提供更好的构建方案。 不过我们还是可以使用xmake来尝试编译他们: ```bash $ xmake create -l rust test create test ... [+]: xmake.lua [+]: src/main.rs [+]: .gitignore create ok! ``` ```bash $ xmake checking for the architecture ... x86_64 checking for the Xcode directory ... /Applications/Xcode.app checking for the SDK version of Xcode ... 10.15 [ 0%]: linking.release test ``` --- --- url: /zh/posts/quickstart-3-run-and-debug.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解如何加载运行编译好的目标程序,以及如何去调试。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 运行生成目标 xmake也提供了run命令,直接运行生成后的可执行文件,用于方便快速的进行测试,例如: ```bash $ xmake run hello xmake! ``` #### 添加运行环境变量 我们也可以在xmake.lua中通过`add_runenvs`接口来添加设置默认运行target程序的环境变量。 所以,对于PATH这种,通过此接口追加值是非常方便的,而且此接口支持多值设置,所以通常就是用来设置带有path sep的多值env。。 ```lua target("test") set_kind("binary") add_files("src/*.c") add_runenvs("PATH", "/tmp/bin", "xxx/bin") add_runenvs("LD_LIBRARY_PATH", "/tmp/lib", "xxx/lib") ``` 更多关于此接口的描述,可以看下文档:[add\_runenvs接口文档](https://xmake.io/zh/) #### 自定义运行逻辑 如果单纯的环境设置,以及默认的加载运行规则不满足需求,我们可以通过定制化`on_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) ``` ### 调试程序 #### 命令行调试 我们也可以传递`-d`参数,调用gdb/lldb等调试器程序,加载目标文件进行调试: ```bash $ xmake run -d ``` 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]$ ``` #### 使用vscode进行断点调试 我们还可以通过[xmake-vscode](https://github.com/xmake-io/xmake-vscode)插件配合vscode来实现对c/c++项目的断点调试支持。 另外我们还需要依赖vscode的C++插件才能进行调试支持,不过由于开发c/c++程序,这个插件几乎是必需,所以并没有太大问题。 就算没有安装此插件,xmake-vscode也会加载lldb/gdb/vsjitdebugger等系统调试器,直接加载调试。 --- --- url: /zh/posts/quickstart-4-basic-project-settings.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解如何编写一些常用的基础xmake.lua描述配置,来实现一些简单的C/C++项目构建管理。 对于大部分小项目,这些配置已经完全足够使用,本系列后期进阶教程中,我会深入详细讲解如果使用一些高级特性来更加灵活定制化地配置项目。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 先来一段最简短的 一行描述即可编译src目录下所有c源文件,然后生成一个名为demo的可执行文件。 ```lua target("demo", {kind = "binary", files = "src/*.c"}) ``` 上面的写法是精简写法,通常我们更推荐使用下面展开式写法: ```lua target("demo") set_kind("binary") add_files("src/*.c") ``` 这两者完全等价,如果配置很简短,可以完全精简成一行,而拆分成多行更加方便灵活配置。 如果没有特殊目的,下文我们都会采用第二段的写法。 ### 配置项目目标类型 通常的C/C++项目生成的目标文件主要有三大类:可执行程序,静态库,动态库。 我们可以通过`set_kind()`配置来设置,分别对应:binary, static, shared 例如,我们想要编译动态库,只需要修改kind: ```lua target("demo") set_kind("shared") add_files("src/*.c") ``` ### 添加宏定义 编译宏的设置,大多数c/c++项目都会用到,一般如果我们设置编译flags传给gcc/clang,都是要配置:`-DXXX` 而在xmake里面,提供了`add_defines()`内置接口来配置: ```lua target("demo") set_kind("shared") add_files("src/*.c") add_defines("XXX") ``` ### 条件配置 那如果我们想在不同编译平台,分别设置不同的宏开关呢?我们可以利用lua内置的if语句很方便的实现: ```lua target("demo") set_kind("shared") add_files("src/*.c") add_defines("XXX") if is_plat("linux", "macosx") then add_defines("YYY") end ``` 我们通过`is_plat()`判断,如果当前编译目标平台是linux或者macosx,那么target会额外增加`-DYYY`宏定义。 ### 全局配置 我们在`target("demo")`下面的所有配置,都属于demo这个target子域,并不是全局的,所以你会看到通常配置上都加了缩进,就是为了凸显作用域的影响范围。 通常如果多个target连续定义,下一个target定义就会自动结束上个target的作用域,每个target的配置完全独立,互不干扰: ```lua target("test1") set_kind("shared") add_files("src/*.c") add_defines("TEST1") target("test2") set_kind("shared") add_files("src/*.c") add_defines("TEST2") ``` 例如,上面的配置两个target,各自拥有自己独立的宏定义:`TEST1`和`TEST2`。 那么,我们要对这两个target,设置共用的宏定义,应该如何配置呢? 每个target下面都配置一遍`add_defines("TEST")`? 当然可以,不过这样就有点冗余了,配置多了就会很难维护,其实我们只需要放置到全局根作用域就行了: ```lua -- 全局设置 add_defines("TEST") if is_arch("arm64", "armv7") then add_defines("ARM") end target("test1") set_kind("shared") add_files("src/*.c") add_defines("TEST1") target("test2") set_kind("shared") add_files("src/*.c") add_defines("TEST2") ``` 在target的外层的所有配置都属于全局配置,我们也可以调用`target_end()`强制结束target子域,切回全局作用域: ```lua target("test1") set_kind("shared") add_files("src/*.c") add_defines("TEST1") target_end() -- 全局设置 add_defines("TEST") if is_arch("arm64", "armv7") then add_defines("ARM") end target("test2") set_kind("shared") add_files("src/*.c") add_defines("TEST2") target_end() ``` ### 添加编译选项 如果有些编译选项,xmake没有提供内置api设置,那么我们可以退化到`add_cflags`, `add_cxflags`, `add_cxxflags`来设置, 不过这就需要用户自己去判断编译平台了,因为并不是所有编译flags每个平台都支持。 比如: ```lua add_cflags("-g", "-O2", "-DDEBUG") if is_plat("windows") then add_cflags("/MT") end ``` 所有选项值都基于gcc的定义为标准,如果其他编译器不兼容(例如:vc),xmake会自动内部将其转换成对应编译器支持的选项值。 用户无需操心其兼容性,如果其他编译器没有对应的匹配值,那么xmake会自动忽略器设置。 我们也可以通过force参数来强制禁用flags的自动检测,直接传入编译器,哪怕编译器有可能不支持,也会设置: ```lua add_cflags("-g", "-O2", {force = true}) ``` 那如何知道,哪些flags检测失败给忽略了呢,带`-v`编译就可以看到,比如: ```bash $ xmake -v checking for the /usr/bin/xcrun -sdk macosx clang ... ok checking for the flags (-Oz) ... ok checking for the flags (-Wno-error=deprecated-declarations) ... ok checking for the flags (-fno-strict-aliasing) ... ok checking for the flags (-Wno-error=expansion-to-defined) ... no ``` 最后备注下这三个api的区别: * `add_cflags`:仅添加C代码相关编译flags * `add_cxflags`:添加C/C++代码相关编译flags * `add_cxxflags`:仅添加C++代码相关编译flags ### 添加库相关设置 一个C/C++库的集成使用,通常需要设置头文件搜索目录,链接库名,库搜索目录,比如: ```lua target("test") set_kind("binary") add_links("pthread") add_includedirs("/usr/local/include") add_linkdirs("/usr/local/lib") ``` 通常,为了保证链接库的依赖顺序,系统库链接通常都会比较靠后,我们通过`add_syslinks()`来专门设置系统库链接,而`add_links()`通常用于非系统库链接: ```lua target("test") set_kind("binary") add_links("A", "B") add_syslinks("pthread") ``` 上面的配置,我们添加了两个第三方链接库:A, B,以及系统库pthread,整个完整的链接顺序是:`-lA -lB -lpthread`,syslinks会放在最后面。 如果你不确定实际的链接顺序,我们可以执行`xmake -v`编译,查看完整的链接参数命令行。 ### 设置语言标准 c标准和c++标准可同时进行设置,例如: ```lua -- 设置c代码标准:c99, c++代码标准:c++11 set_languages("c99", "c++11") ``` 注:并不是设置了指定的标准,编译器就一定会按这个标准来编译,毕竟每个编译器支持的力度不一样,但是xmake会尽最大可能的去适配当前编译工具的支持标准。 例如:windows下vs的编译器并不支持按c99的标准来编译c代码,只能支持到c89,但是xmake为了尽可能的支持它,所以在设置c99的标准后, xmake会强制按c++代码模式去编译c代码,从一定程度上解决了windows下编译c99的c代码问题。 ### 设置编译优化 xmake提供了几种内置的编译优化配置:none, fast, faster, fastest, smallest, aggressive,来实现各种级别的编译优化。 ```lua set_optimize("fastest") ``` 如果用户通过flags来设置,还需额外考虑不同编译器的不同编译选项,xmake对其进行了内部映射处理,极大程度方便用户提供跨平台性。 如果想查看详细的映射规则,可以到xmake的官方文档进行查看:[编译优化设置](https://xmake.io/zh/) ### 调试和发布模式 即使xmake提供了`set_optimize`简化了不同编译器的复杂配置,但是对于不同的编译模式: debug/release,还是要自己做一些繁琐的判断和配置: ```lua if is_mode("debug") then set_symbols("debug") set_optimize("none") end if is_mode("release") then set_symbols("hidden") set_strip("all") if is_plat("iphoneos", "android") then set_optimize("smallest") else set_optimize("fastest") end end ``` 这些看似常用的设置,如果每个项目都来一遍,那也很繁琐了,导致xmake.lua不够精简可读,因此xmake提供了一些常用内置规则来简化设置: ```lua add_rules("mode.release", "mode.debug") ``` 只需这一行即可,效果是完全一致,用户还可以基于此在做一些额外的定制化配置来改写: ```lua add_rules("mode.release", "mode.debug") if is_mode("release") then set_optimize("fastest") end ``` 比如我想在release模式下,强制启用fastest编译优化,既然有了模式配置,那我们怎么切换到debug模式编译呢?(默认是release编译) 答案: ```lua xmake f -m debug; xmake ``` ### 添加源文件 最后,我们在介绍下xmake最常用,也最为强大的设置之一,也就是对编译源文件的配置管理:`add_files()`。 我们可以用这个接口,添加各类xmake支持的源文件,比如:c/c++, asm, objc, swift, go, dlang等源文件,甚至是:`.obj`, `.a/.lib`等二进制对象和库文件。 例如: ```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"}}) ``` ### 删除指定源文件 既然讲到了添加源文件,那么如何删除,我们也顺带着讲下吧,我们只需要通过`del_files()`接口,就可以从前面`add_files`接口添加的文件列表中,删除指定的文件,例如: ```lua target("test") add_files("src/*.c") del_files("src/test.c") ``` 上面的例子,可以从`src`目录下添加除`test.c`以外的所有文件,当然这个也可以通过`add_files("src/*.c|test.c")`来达到相同的目的,但是这种方式更加灵活。 例如,我们可以条件判断来控制删除哪些文件,并且此接口也支持`add_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`(就是说,不删除这个文件)。 --- --- url: /zh/posts/quickstart-5-build-android.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文主要详细讲解如何通过xmake编译可在android下运行的库和可执行程序。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 准备工作 首先,我们需要先准备好编译android native库必须的ndk工具链,如果还没有可以从官网下载解压即可:[Android NDK](https://developer.android.com/ndk/) 如果是为了获取更好的向下兼容性,可以选择r16版本,因为这个是最后一个支持armeabi的版本,如果没什么特别需求,可以直接下载最新版。 ### NDK集成和编译 #### 手动配置NDK 我们只需要将解压后ndk目录路径传递给xmake完成配置,可以直接编译了,例如: ```bash $ xmake f -p android --ndk=~/downloads/android-ndk-r19c $ xmake ``` 其中,`-p android`用于切换到android平台,因为如果不指定平台,默认会编译当前主机平台的target程序。 通常,如果没特殊需求,上面的配置就可以完成android native程序的编译,目前xmake内置支持:binary, static, shared这三种基础target类型文件的生成,分别对应可执行程序,.a静态库,.so动态库。 #### NDK路径的全局配置 `xmake f/config`命令仅仅是针对当前项目的配置,如果经常跨平台编译和配置切换都要重新设置一遍ndk路径,那么还是稍显繁琐。 我们可以通过`xmake g/global`全局配置命令来设置它,确保永久生效。 ```bash $ xmake g --ndk=~/xxx/android-ndk-r19c ``` 我们也可以通过设置`ANDROID_NDK_HOME`全局环境变量来确保永久生效,这跟上述命令配置的效果是差不多的。 #### NDK路径的自动探测 通常情况下即使没有配置ndk路径,xmake还是会尝试默认检测一些常用路径,比如在macos下会自动探测是否存在以下路径: ``` ~/Library/Android/sdk/ndk-bundle ``` 这是mac下装完android studio自动创建的sdk目录,以及ndk的常用放置路径。 或者尝试从ANDROID\_NDK\_HOME这种环境变量中探测,如果存在的话。 如果能探测到,也就没必要再额外手动配置了。 ### C++ STL库配置切换 首先,我们先来介绍下,ndk提供的三种stl库版本 * stlport:早期ndk内置的stl库,现在基本已废弃 * gnustl:ndk r16b之前主要使用的stl库,但是自从r16b之后,也已经被google去掉了 * llvm-c++:r16b之后较新的ndk内置的stl库 因此,我们在编译android库的时候,需要根据自己的需求,选用stl,以及选用合适的ndk版本,而xmake通常会尽可能默认使用llvm-c++库,如果发现当前ndk版本比较老,会尝试退化到gnustl上去。 用户也可以手动修改stl库的版本,例如: ```bash $ xmake f -p android --ndk=xxxx --ndk_cxxstl=gnustl_shared ``` 具体,关于ndk\_cxxstl选项的配置值,可以敲help查看,`xmake f --help`,主要就是: * `llvmstl_static` * `llvmstl_shared` * `gnustl_static` * `gnustl_shared` * `stlport_static` * `stlport_shared` ### API版本设置 如果在编译过程中,报出一些libc库符号找不到,通常有可能是api版本没设置对,因为有些libc函数,只有在高版本api下才存在。 这个时候,我们可以通过尝试手动修改api版本来解决: ```bash $ xmake f -p android --ndk=xxx --ndk_sdkver=16 ``` ### arch的编译切换 目前xmake提供 `armv7-a`, `arm64-v8a`, `armv5te`, `mips`, `mips64`, `i386`,`x86_64`这些架构的配置编译,如果没有指定arch,那么默认会使用armv7架构。 手动修改arch方式如下: ```bash $ xmake f -p android --ndk=xxx -a arm64-v8a ``` ### Android相关配置设置 如果项目中需要配置一些只有android平台才有的编译设置,比如添加特定宏开关,链接库等,可以在xmake.lua中,通过`is_plat("android")`来判断处理。 ```lua target("test") set_kind("shared") add_files("src/*.c") if is_plat("android") then add_defines("ANDROID") add_syslinks("log") end ``` ### FAQ #### 遇到一些libc/stl库头文件找不到怎么办? 可以尝试修改stl库版本,和api版本来解决,比如ndk r16b 推荐使用gnustl库,因为这个版本的llvmc++库刚集成进去不久,问题比较多,使用过程中容易遇到各种编译问题。 ```bash $ xmake f -p android --ndk=xxxx --ndk_cxxstl=gnustl_shared --ndk_sdkver=16 ``` #### 编译生成的可执行程序在设备上运行不起来? 通常是api版本设置太高,导致的不兼容问题,可以尝试调低api版本。 --- --- url: /zh/posts/quickstart-6-build-qt-project.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 xmake完全支持对Qt5项目的维护和构建,通过本文将会带你了解如何通过xmake来维护各种类型的Qt项目。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 前言 Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它有自己的IDE程序:qt creator,也有自己的构建程序:qmake,似乎新版本开始打算全面切到cmake来维护了。 尽管如此,xmake还是对Qt的开发做了支持,搭配上xmake-vscode/xmake-idea等插件,使用户可以在自己熟悉的编辑器和IDE上集成和开发Qt程序,并且在不同平台上提供一致的开发体验。 ### 准备构建环境 首先,我们得准备好Qt开发环境,如果还没安装Qt SDK,那么到qt的官网登录下载安装包:https://www.qt.io/,或者自己拉取qt源码,编译静态版本sdk和工具链。 通常情况,如果是采用官方提供的QT SDK安装包,并且安装目录采用的默认路径,那么即使不配置QT SDK路径,xmake也会尝试去检测它,一般都是能检测到的,如果检测不到,我们可以尝试手动配置下它: ```bash $ xmake f --qt=/home/xxx/qtsdk ``` 或者设置到全局路径,避免每次编译切换都要配置一遍: ```bash $ xmake g --qt=/home/xxx/qtsdk ``` ### 创建模板工程 xmake内置了各种Qt项目的空工程模板,我们可以通过`xmake create`命令来快速创建它们。 注:由于xmake的master最新版本,也就是还未发布的v2.2.9版本对Qt的模板和构建规则进行了升级,因此本文主要讲解的都是基于最新版本来讲解, 而之前的老模版和规则也是向下兼容的,如果想要继续了解,可以查看相关文档:[Qt项目开发文档](https://xmake.io/zh/) #### 创建QuickApp应用程序 我们先来创建一个带qml的quickapp空工程,只需要敲如下命令: ```bash $ xmake create -t qt.quickapp test create test ... [+]: xmake.lua [+]: src/main.qml [+]: src/main.cpp [+]: src/qml.qrc [+]: .gitignore create ok! ``` xmake会生成带有xmake.lua的Qt项目,xmake.lua内容也很简单: ```lua target("test") add_rules("qt.quickapp") add_headerfiles("src/*.h") add_files("src/*.cpp") add_files("src/qml.qrc") ``` 除了源文件的添加,其他基本上都跟之前的可执行程序项目没什么不同,唯一的区别就是通过`add_rules("qt.quickapp")`这个内置的Qt构建规则来代替`set_kind("binary")`。 其实`qt.quickapp`规则内部最终还是设置了binary类型,只不过在此基础上额外增加了一些只有Qt才需要的构建规则,比如:特定links,flags还有includedirs等。 接下来,我们尝试编译下这个项目: ```bash $ xmake checking for the architecture ... x86_64 checking for the Xcode directory ... /Applications/Xcode.app checking for the SDK version of Xcode ... 10.15 checking for the Qt SDK directory ... /Users/ruki/Qt5.13.2/5.13.2/clang_64 checking for the Qt SDK version ... 5.13.2 [ 0%]: ccache compiling.release src/main.cpp [ 49%]: compiling.qt.qrc src/qml.qrc [100%]: linking.release test ``` 其中`*.qrc`文件的构建规则也是在`qt.quickapp`的构建规则里面维护的,所以只有设置了这个rule,才能正常编译qrc文件。 最后,我们尝试运行下看看: ```bash $ xmake run ``` 运行效果如下: ![](/assets/img/guide/qt_quickapp.png) #### 创建WidgetApp应用程序 创建一个widgetapp工程跟上文的quickapp方式基本一致,只需要改下模板名即可: ```bash $ xmake create -t qt.widgetapp test ``` 里面xmake.lua的内容看起来,也仅仅就是把`qt.quickapp`规则改成了`qt.widgetapp`规则,另外,ui描述文件从`.qrc`变成了`.ui`,其他并无区别。 ```lua target("qt_widgetapp") add_rules("qt.widgetapp") add_files("src/*.cpp") add_files("src/mainwindow.ui") add_files("src/mainwindow.h") -- 添加带有 Q_OBJECT 的meta头文件 ``` 运行效果如下: ![](/assets/img/guide/qt_widgetapp.png) #### 创建静态链接版本应用程序 默认通过qt官网下载的sdk,都是基于动态库的,如果用户用的是自己拉取qt源码然后编译的static版本qt sdk,那么创建的qt工程类型也必须对应static版本,因为两者来处理链接上会有不同的逻辑。 对于模板名,后面追加下`_static`来创建: ```bash $ xmake create -t qt.widgetapp_static test ``` 创建的就是基于静态QtSdk的WidgetApp工程,其里面的构建规则,也会改成`add_rules("qt.widgetapp_static")`,其他并无不同,QuickApp项目也是如此。 #### 创建其他Qt项目 除了QuickApp和WidgetApp项目,xmake还支持其他Qt项目的创建和编译,比如:终端程序,基于Qt的静态库和动态库等。 具体的工程模板,我们可以进入help菜单查看里面的模板列表: ```bash $ xmake create --help Usage: $xmake create [options] [target] Create a new project. Options: -t TEMPLATE, --template=TEMPLATE Select the project template id or name of the given language. (default: console) - console: c++, go, dlang, cuda, rust, swift, objc, c, objc++ - qt.console: c++ - qt.quickapp: c++ - qt.quickapp_static: c++ - qt.shared: c++ - qt.static: c++ - qt.widgetapp: c++ - qt.widgetapp_static: c++ ``` 更多其他Qt项目的使用说明,可以查看xmake的官方文档:[Qt项目构建文档](https://xmake.io/zh/) ### 运行和断点调试 我们可以通过`xmake run -d`命令来加载gdb/lldb调试程序,或者搭配xmake-vscode插件的断点调试支持,来开发和调试Qt程序。 这块可以阅读前文:[xmake从入门到精通3:运行和调试目标程序](https://tboox.org/cn/2019/11/09/quickstart-3-run-and-debug/) 另外,如果是win平台,我们也可以通过生成vs proj,然后通过vs自带的调试功能,进行断点调试,更加方便: ```bash $ xmake project -k vsxmake ``` 生成基于xmake的vs工程后,打开vs工程,点击调试运行即可: ![](/assets/img/manual/qt_vs.png) ### 开发Android程序 xmake目前是完全支持编译Android版本的Qt项目,整个Qt项目包括xmake.lua完全跟前面的例子一致,并不需要做特别的设置。 我们需要做的仅仅是,切换到android的编译平台去编译它,不过由于要生成apk包,在执行xmake编译后,qt构建规则会自动对android程序做一个部署deploy步骤,也就是调用qt内部的androiddeployqt程序去生成apk包。 因此除了需要android ndk,我们还需要额外依赖android sdk,通过设置`--android_sdk`参数对其指定下: ```bash $ 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 ``` 上面的配置和构建过程就可以很方便的将之前的QuickApp和WidgetApp项目编译成Android App,另外qt规则内部还对android版本定制了install程序,可以很方便的安装qt apk到设备。 ```bash $ xmake install installing appdemo ... installing build/android/armv7-a/release/appdemo.apk .. success install ok! ``` 安装和运行后的效果如下: ![](https://user-images.githubusercontent.com/151335/57430932-c7261000-7263-11e9-8886-eff07208d0d8.jpeg) 关于如何配置Android编译环境,可阅读前文:[xmake从入门到精通5:Android平台编译详解](https://tboox.org/cn/2019/11/15/quickstart-5-build-android/) ### 编辑器和IDE集成 xmake也提供了对各大常用编辑器的插件集成支持,配合这些插件,就可以在自己最熟悉的编辑器上开发和构建Qt程序。 #### 在vscode上开发和调试Qt程序 插件地址:[xmake-vscode](https://github.com/xmake-io/xmake-vscode) #### 在Sublime Text上开发Qt程序 插件地址:[xmake-sublime](https://github.com/xmake-io/xmake-sublime) #### 在Idea/CLion/Android Studio上开发Qt程序 插件地址:[xmake-idea](https://github.com/xmake-io/xmake-idea) #### 在VisualStudio里面开发和调试Qt程序 也就是刚上面提到的通过生成vs proj方式来集成xmake: ```bash $ xmake project -k vsxmake ``` 生成基于xmake的vs工程后,打开vs工程,点击调试运行即可: ![](/assets/img/manual/qt_vs.png) 这块,具体详情,可以查看插件文档:[使用xmake生成vs工程](https://xmake.io/zh/) --- --- url: /zh/posts/quickstart-7-build-cuda-project.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文我们会详细介绍下如何通过xmake来构建cuda程序以及与c/c++程序混合编译。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 准备环境 首先,我们需要安装NVIDIA提供的Cuda Toolkit SDK工具,其相关说明以及安装文档,可参考官方文档:[CUDA Toolkit Documentation](http://docs.nvidia.com/cuda/index.html)。 下载安装好Cuda SDK后,在macosx上会默认安装到`/Developer/NVIDIA/CUDA-x.x`目录下,Windows上可以通过`CUDA_PATH`的环境变量找到对应的SDK目录,而 Linux下默认会安装到`/usr/local/cuda`目录下。 通常,xmake都能自动检测到默认的cuda安装环境,并不需要做任何操作,只需要执行`xmake`命令就可以自动完成编译,当然如果找不到SDK,我们也可以手动指定Cuda SDK环境目录: ```console $ xmake f --cuda=/usr/local/cuda-9.1/ ``` 或者通过`xmake g/global`命令切到全局设置,避免每次切换编译模式都要重新配置一遍。 ```console $ xmake g --cuda=/usr/local/cuda-9.1/ ``` 如果想要测试xmake对当前cuda环境的探测支持,可以直接运行: ```bash $ xmake l detect.sdks.find_cuda { linkdirs = { "/Developer/NVIDIA/CUDA-10.2/lib/stubs", "/Developer/NVIDIA/CUDA-10.2/lib" }, bindir = "/Developer/NVIDIA/CUDA-10.2/bin", sdkdir = "/Developer/NVIDIA/CUDA-10.2", includedirs = { "/Developer/NVIDIA/CUDA-10.2/include" } } ``` 大家也可以帮忙贡献相关检测代码[find\_cuda.lua](https://github.com/xmake-io/xmake/blob/master/xmake/modules/detect/sdks/find_cuda.lua)来改进xmake的检测机制。 ### 创建工程 接下来,我们就可以创建一个空工程来快速体验下了,xmake自带了cuda的工程模板,只需要指定对应的语言即可创建cuda项目: ```bash $ xmake create -l cuda test create test ... [+]: xmake.lua [+]: src/main.cu [+]: .gitignore create ok! ``` 默认创建的cuda工程,就是一个最简单的基于Cuda的hello world工程,其源码结构如下: ``` ├── src │   └── main.cu └── ke.lua ``` 而xmake.lua里面的内容我们也可以简单看下: ```lua -- define target target("test") 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") ``` 可以看到,除了最基本的.cu源文件添加,跟其他c/c++项目唯一的区别就是多了个`add_cugencodes()`用来设置cuda需要的gencodes,关于这块,下面会详细讲解。 ### 编译项目 工程创建好后,只需要简单的执行xmake即可完成编译。 ```bash $ xmake [00%]: ccache compiling.release src/main.cu [99%]: devlinking.release test_gpucode.cu.o [100%]: linking.release test ``` 需要注意的是:从v2.2.7版本开始,xmake默认构建会启用device-link的构建行为,也就是说,现在编译过程中,会额外增加一步device-link过程: ```bash [100%]: devlinking.release test_gpucode.cu.o ``` 按照官方的说法,启用device-link设备代码链接的主要优点是可以为您的应用程序提供更传统的代码结构,尤其是在C++中,在现有项目结构不变的前提下,控制每个构建和链接步骤,方便快速的启用GPU代码,实现混合编译。 关于这块可参看NVIDIA的官方描述:[Separate Compilation and Linking of CUDA C++ Device Code](https://devblogs.nvidia.com/separate-compilation-linking-cuda-device-code/)) 如果要禁用device-link的构建逻辑,可以通过`add_values("cuda.devlink", false)` 来设置禁用它。 当然,我们也可以尝试直接运行这个cuda程序: ```bash $ xmake run ``` ### 项目设置 并且如果设置了里面值为native,那么xmake会自动探测当前主机的cuda设备对应的gencode。 #### add\_cuflags 这个接口主要用于添加cu代码相关的编译选项,我们如果还需要一些更加定制化的设置flags,那么就可以调用`add_cuflags`来直接设置更加原始的编译选项,就好比c/c++中的`add_cxflags`。 例如: ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") ``` #### add\_culdflags 这个接口主要用于添加cuda设备链接选项,由于上文所说,2.2.7之后,xmake对于cuda程序的默认构建行为会使用device-link,这个阶段如果要设置一些链接flags,则可以通过这个接口来设置。 因为最终的程序链接,会使用ldflags,不会调用nvcc,直接通过gcc/clang等c/c++链接器来链接,所以device-link这个独立的链接阶段的flags设置,通过这个接口来完成。 ```lua add_culdflags("-gencode arch=compute_30,code=sm_30") ``` #### add\_cugencodes `add_cugencodes()`接口其实就是对`add_cuflags("-gencode arch=compute_xx,code=compute_xx")`编译flags设置的简化封装,其内部参数值对应的实际flags映射关系如下: ```lua - compute_xx --> `-gencode arch=compute_xx,code=compute_xx` - sm_xx --> `-gencode arch=compute_xx,code=sm_xx` - sm_xx,sm_yy --> `-gencode arch=compute_xx,code=[sm_xx,sm_yy]` - compute_xx,sm_yy --> `-gencode arch=compute_xx,code=sm_yy` - compute_xx,sm_yy,sm_zz --> `-gencode arch=compute_xx,code=[sm_yy,sm_zz]` - native --> match the fastest cuda device on current host, eg. for a Tesla P100, `-gencode arch=compute_60,code=sm_60` will be added, if no available device is found, no `-gencode` flags will be added ``` 例如: ```lua add_cugencodes("sm_30") ``` 就等价为 ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") add_culdflags("-gencode arch=compute_30,code=sm_30") ``` 是不是上面的更加精简些,这其实就是个用于简化设置的辅助接口。 而如果我们设置了native值,那么xmake会自动探测当前主机的cuda设备,然后快速匹配到它对应的gencode设置,自动追加到整个构建过程中。 例如,如果我们主机目前的GPU是Tesla P100,并且能够被xmake自动检测到,那么下面的设置: ```lua add_cugencodes("native") ``` 等价于: ```lua add_cugencodes("sm_60") ``` ### Cuda/C/C++的混合编译 对于混合编译,我们只需要通过`add_files`接口继续加上对应的c/c++代码文件就行了,是不是很简单? ```lua target("test") set_kind("binary") add_files("src/*.cu") add_files("src/*.c", "src/*.cpp") add_cugencodes("native") ``` ### 编译设置 nvcc在编译内部的c/c++代码时候,其实会调用主机环境的c/c++编译器来编译,比如linux下会默认使用gcc/g++,macos下默认使用clang/clang++,windows上默认使用cl.exe。 如果想要让nvcc采用其他的编译器,比如在linux下改用clang作为默认的c/c++编译器,则需要指定`--ccbin=`参数设置,这块可以看下:[compiler-ccbin](https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#file-and-path-specifications-compiler-bindir) 而在xmake中,也对其进行了支持,只需要设置`xmake f --cu-ccbin=clang` 就可以切换到其他编译器。 还有两个跟cuda相关的编译参数,我就简单介绍下: ```bash xmake f --cu=nvcc --cu-ld=nvcc ``` 其中`--cu`用来设置.cu代码的编译器,默认就是nvcc,不过clang现在也支持对.cu代码的编译,可以切换设置来尝试,`--cu-ld`是设置device-link的链接器,而最终的整体程序link过程,还是用的`--ld`来链接的。 --- --- url: /zh/posts/quickstart-8-switch-build-mode.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 本文我们会详细介绍下如何在项目构建过程中切换debug/release等常用构建模式,以及自定义其他编译模式。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 调试和发布模式 通常,如果我们是通过`xmake create`命令创建的项目,会在xmake.lua里面自动添加一行编译规则的配置,如下: ```lua add_rules("mode.release", "mode.debug") target("hello") set_kind("binary") add_files("src/*.c") ``` 通过`add_rules`接口,我们默认添加了release和debug两个常用的内置规则,它们会在编译的时候附带上对应模式相关的一些编译flags,来开启优化用于发布或者调试编译。 如果仅仅执行了`xmake`命令,没有额外的配置,那么默认就会是release编译,等价于: ```bash $ xmake f -m release $ xmake [ 0%]: ccache compiling.release src/main.cpp [100%]: linking.release test build ok! ``` 如果我们要切换到debug编译模式,只需要: ```bash $ xmake f -m debug $ xmake [ 0%]: ccache compiling.debug src/main.cpp [100%]: linking.debug test build ok! ``` 上面的`-m/--mode=`参数就是用来设置编译模式,会跟`mode.release`和`mode.debug`这两个规则做关联。 那么,他们是如何关联上的呢?我们可以先来看下这两个规则的内部实现: ```lua rule("mode.debug") after_load(function (target) if is_mode("debug") then if not target:get("symbols") then target:set("symbols", "debug") end if not target:get("optimize") then target:set("optimize", "none") end end end) rule("mode.release") after_load(function (target) if is_mode("release") then if not target:get("symbols") and target:targetkind() ~= "shared" then target:set("symbols", "hidden") end if not target:get("optimize") then if is_plat("android", "iphoneos") then target:set("optimize", "smallest") else target:set("optimize", "fastest") end end if not target:get("strip") then target:set("strip", "all") end end end) ``` 可以看到,在target被加载阶段,xmake会去判断用户对`xmake f --mode=xxx`的参数配置,如果通过`is_mode()`接口获取到是debug模式,那么会禁用相关优化并且启用符号输出。 而如果是release模式,那么会开启编译优化并且strip掉所有调试符号。 ### 定制化的模式配置 当然,内置的这两规则默认设置的这些编译配置,只能满足大部分场景的常规需求,如果用户想要在不同的编译模式下定制化一些个人的编译配置,那么需要自己在xmake.lua做判断。 例如,我们想在release下也启用调试符号,那么只需要: ```lua if is_mode("release") then set_symbols("debug") end ``` 或者额外增加一些编译flags: ```lua if is_mode("release") then add_cflags("-fomit-frame-pointer") end ``` 注:如果用户自己的配置和`mode.release`内置的配置冲突,会优先使用用户的设置。 当然,我们也可以完全不去通过`add_rules("mode.debug", "mode.release")`添加默认的配置规则,让用户完全自己控制模式配置: ```lua -- 如果当前编译模式是debug if is_mode("debug") then -- 添加DEBUG编译宏 add_defines("DEBUG") -- 启用调试符号 set_symbols("debug") -- 禁用优化 set_optimize("none") end -- 如果是release或者profile模式 if is_mode("release", "profile") then -- 如果是release模式 if is_mode("release") then -- 隐藏符号 set_symbols("hidden") -- strip所有符号 set_strip("all") -- 忽略帧指针 add_cxflags("-fomit-frame-pointer") add_mxflags("-fomit-frame-pointer") -- 如果是profile模式 else -- 启用调试符号 set_symbols("debug") end -- 添加扩展指令集 add_vectorexts("sse2", "sse3", "ssse3", "mmx") end ``` ### 其他内置模式规则 通过上文的例子,我们看到除了debug/release模式,还加了个profile模式的配置判断,其实xmake也提供了对应的内置模式,还有哪些,我们具体来看下: #### mode.debug 为当前工程xmake.lua添加debug编译模式的配置规则,例如: ```lua add_rules("mode.debug") ``` 相当于: ```lua if is_mode("debug") then set_symbols("debug") set_optimize("none") end ``` 我们可以通过:`xmake f -m debug`来切换到此编译模式。 #### mode.release 为当前工程xmake.lua添加release编译模式的配置规则,例如: ```lua add_rules("mode.release") ``` 相当于: ```lua if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end ``` 我们可以通过:`xmake f -m release`来切换到此编译模式。 #### mode.check 为当前工程xmake.lua添加check编译模式的配置规则,一般用于内存检测,例如: ```lua add_rules("mode.check") ``` 相当于: ```lua if is_mode("check") then set_symbols("debug") set_optimize("none") 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 if is_mode("profile") then set_symbols("debug") add_cxflags("-pg") add_ldflags("-pg") end ``` 我们可以通过:`xmake f -m profile`来切换到此编译模式。 #### mode.coverage 为当前工程xmake.lua添加coverage编译模式的配置规则,一般用于覆盖分析,例如: ```lua add_rules("mode.coverage") ``` 相当于: ```lua if is_mode("coverage") then add_cxflags("--coverage") add_mxflags("--coverage") add_ldflags("--coverage") end ``` 我们可以通过:`xmake f -m coverage`来切换到此编译模式。 注:生成的gcno文件一般都是个obj所在目录对应的哦,因此需要从build目录下去找。 ### 扩展自己的编译模式 xmake的模式配置,并没有固定值,用户可以随意传入和配置,只要`xmake f -m/--mode=xxx`传入的模式值和xmake.lua里面的`is_mode("xxx")`能对应上就行。 比如,我们设置了一个自己独有的编译模式`my_mode`,可以直接在命令行配置切换; ```bash $ xmake f -m my_mode $ xmake [ 0%]: ccache compiling.my_mode src/main.cpp [100%]: linking.my_mode test build ok! ``` 然后xmake.lua里面对相应的值进行判断即可: ```lua if is_mode("my_mode") then add_defines("ENABLE_MY_MODE") end ``` ### 使用模式变量 我们也可以直接在配置值中传递模式变量`$(mode)`,比如根据不同模式选择链接不同的库: ```lua target("test") set_kind("binary") add_files("src/*.c") add_links("xxx_$(mode)") ``` 上面的配置,如果是调试模式编译就会选择链接:`libxxx_debug.a`库,而release下就会链接`libxxx_release.a`,当然,我们也可以设置到库搜索路径中,根据目录来选择对应的库。 ```lua target("test") set_kind("binary") add_files("src/*.c") add_linkdirs("lib/$(mode)") add_links("xxx") ``` 另外,我们可以通过`get_config("mode")`直接获取到传入的模式配置值,并且这几种获取方式,在自定义脚本也是同样有效的哦,例如: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load(function (target) if is_mode("release") then print(get_config("mode"), "$(mode)") end end) ``` --- --- url: /zh/posts/quickstart-9-cross-compile.md --- xmake是一个基于Lua的轻量级现代化c/c++的项目构建工具,主要特点是:语法简单易上手,提供更加可读的项目维护,实现跨平台行为一致的构建体验。 除了win, linux, macOS平台,以及android, ios等移动端平台的内建构建支持,xmake也支持对各种其他工具链的交叉编译支持,本文我们将会详细介绍下如何使用xmake进行交叉编译。 * [项目源码](https://github.com/xmake-io/xmake) * [官方文档](https://xmake.io/zh/) ### 交叉编译工具链简介 通常,如果我们需要在当前pc环境编译生成其他设备上才能运行的目标文件时候,就需要通过对应的交叉编译工具链来编译生成它们,比如在win/macos上编译linux的程序,或者在linux上编译其他嵌入式设备的目标文件等。 通常的交叉编译工具链都是基于gcc/clang的,大都具有类似如下的结构: ``` /home/toolchains_sdkdir - bin - arm-linux-armeabi-gcc - arm-linux-armeabi-ld - ... - lib - libxxx.a - include - xxx.h ``` 每个工具链都有对应的include/lib目录,用于放置一些系统库和头文件,例如libc, stdc++等,而bin目录下放置的就是编译工具链一系列工具。例如: ``` arm-linux-armeabi-ar arm-linux-armeabi-as arm-linux-armeabi-c++ arm-linux-armeabi-cpp arm-linux-armeabi-g++ arm-linux-armeabi-gcc arm-linux-armeabi-ld arm-linux-armeabi-nm arm-linux-armeabi-strip ``` 其中`arm-linux-armeabi-`前缀就是cross,通过用来标示目标平台和架构,主要用于跟主机自身的gcc/clang进行区分。 里面的gcc/g++就是c/c++的编译器,通常也可以作为链接器使用,链接的时候内部会去调用ld来链接,并且自动追加一些c++库。 cpp是预处理器,as是汇编器,ar用于生成静态库,strip用于裁剪掉一些符号信息,使得目标程序会更加的小。nm用于查看导出符号列表。 ### 自动探测和编译 如果我们的交叉编译工具链是上文的结构,xmake会自动检测识别这个sdk的结构,提取里面的cross,以及include/lib路径位置,用户通常不需要做额外的参数设置,只需要配置好sdk根目录就可以编译了,例如: ```bash $ xmake f -p cross --sdk=/home/toolchains_sdkdir $ xmake ``` 其中,`-p cross`用于指定当前的平台是交叉编译平台,`--sdk=`用于指定交叉工具链的根目录。 注:我们也可以指定`-p linux`平台来配置交叉编译,效果是一样的,唯一的区别是额外标识了linux平台名,方便xmake.lua里面通过`is_plat("linux")`来判断平台。 这个时候,xmake会去自动探测gcc等编译器的前缀名cross:`arm-linux-armeabi-`,并且编译的时候,也会自动加上`链接库`和`头文件`的搜索选项,例如: ``` -I/home/toolchains_sdkdir/include -L/home/toolchains_sdkdir/lib ``` 这些都是xmake自动处理的,不需要手动配置他们。 ### 手动配置编译 如果上面的自动检测对某些工具链,还无法完全通过编译,就需要用户自己手动设置一些交叉编译相关的配置参数,来调整适应这些特殊的工具链了,下面我会逐一讲解如何配置。 #### 设置工具链bin目录 对于不规则工具链目录结构,靠单纯地[--sdk](https://xmake.io/zh/)选项设置,没法完全检测通过的情况下,可以通过这个选项继续附加设置工具链的bin目录位置。 例如:一些特殊的交叉工具链的,编译器bin目录,并不在 `/home/toolchains_sdkdir/bin` 这个位置,而是独立到了 `/usr/opt/bin` 这个时候,我们可以在设置了sdk参数的基础上追加bin目录的参数设置,来调整工具链的bin目录。 ```bash $ xmake f -p linux --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin $ xmake ``` #### 设置交叉工具链工具前缀 像aarch64-linux-android-这种,通常如果你配置了--sdk或者--bin的情况下,xmake会去自动检测的,不需要自己手动设置。 但是对于一些极特殊的工具链,一个目录下同时有多个cross前缀的工具bin混在一起的情况,你需要手动设置这个配置,来区分到底需要选用哪个bin。 例如,toolchains的bin目录下同时存在两个不同的编译器: ``` /opt/bin - armv7-linux-gcc - aarch64-linux-gcc ``` 我们现在想要选用armv7的版本,那么我们可以追加`--cross=`配置编译工具前缀名,例如: ```bash $ xmake f -p linux --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- ``` #### 设置c/c++编译器 如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: ```bash $ xmake f -p linux --sdk=/user/toolsdk --cc=armv7-linux-clang --cxx=armv7-linux-clang++ ``` 当然,我们也可以指定编译器全路径。 `--cc`用于指定c编译器名,`--cxx`用于指定c++编译器名。 注:如果存在CC/CXX环境变量的话,会优先使用当前环境变量中指定的值。 如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么编译器工具检测就会失败。 这个时候我们可以通过: ```bash xmake f --cxx=clang++@/home/xxx/c++mips.exe ``` 设置c++mips.exe编译器作为类clang++的使用方式来编译。 也就是说,在指定编译器为`c++mips.exe`的同时,告诉xmake,它跟clang++用法和参数选项基本相同。 #### 设置c/c++连接器 如果还要继续细分选择链接器,则继续追加相关链接器选项,例如: ```bash $ xmake f -p linux --sdk=/user/toolsdk --ld=armv7-linux-clang++ --sh=armv7-linux-clang++ --ar=armv7-linux-ar ``` ld指定可执行程序链接器,sh指定共享库程序链接器,ar指定生成静态库的归档器。 注:如果存在LD/SH/AR环境变量的话,会优先使用当前环境变量中指定的值。 #### 设置头文件和库搜索目录 如果sdk里面还有额外的其他include/lib目录不在标准的结构中,导致交叉编译找不到库和头文件,那么我们可以通过`--includedirs`和`--linkdirs`来追加搜索路径,然后通过`--links`添加额外的链接库。 ```bash $ xmake f -p linux --sdk=/usr/toolsdk --includedirs=/usr/toolsdk/xxx/include --linkdirs=/usr/toolsdk/xxx/lib --links=pthread ``` 注:如果要指定多个搜索目录,可以通过`:`或者`;`来分割,也就是不同主机平台的路径分隔符,linux/macos下用`:`,win下用`;`。 #### 设置编译和链接选项 我们也可以根据实际情况通过`--cflags`, `--cxxflags`,`--ldflags`,`--shflags`和`--arflags`额外配置一些编译和链接选项。 * cflags: 指定c编译参数 * cxxflags:指定c++编译参数 * cxflags: 指定c/c++编译参数 * asflags: 指定汇编器编译参数 * ldflags: 指定可执行程序链接参数 * shflags: 指定动态库程序链接参数 * arflags: 指定静态库的生成参数 例如: ```bash $ xmake f -p linux --sdk=/usr/toolsdk --cflags="-DTEST -I/xxx/xxx" --ldflags="-lpthread" ``` ### mingw工具链 使用mingw工具链编译,其实也是交叉编译,但是由于这个比较常用,xmake专门增加了一个mingw的平台来快速处理使用mingw工具链的编译。 因此,xmake对mingw的工具链检测会更加完善,在macos下,基本上连sdk路径都不需要配置,也能直接检测到,只需要切到mingw平台编译即可。 ```bash $ xmake f -p mingw $ xmake -v configure { ld = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ ndk_stdcxx = true plat = mingw mingw = /usr/local/opt/mingw-w64 buildir = build arch = x86_64 xcode = /Applications/Xcode.app mode = release cxx = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc cross = x86_64-w64-mingw32- theme = default kind = static ccache = true host = macosx clean = true bin = /usr/local/opt/mingw-w64/bin } [ 0%]: ccache compiling.release src/main.cpp /usr/local/bin/ccache /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc -c -fvisibility=hidden -O3 -m64 -o build/.objs/test/mingw/x86_64/release/src/main.cpp.obj src/main.cpp [100%]: linking.release test.exe /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ -o build/mingw/x86_64/release/test.exe build/.objs/test/mingw/x86_64/release/src/main.cpp.obj -s -fvisibility=hidden -m64 build ok! ``` 这里我们追加了`-v`参数,看了下详细的编译命令和检测到的mingw工具链配置值,其中cross被自动检测为:`x86_64-w64-mingw32-`,bin目录也被自动检测到了,还有编译器和链接器也是。 尽管在linux/win上还没法自动检测到sdk路径,我们也可以手动指定sdk路径,需要注意的是,xmake为mingw专门提供了一个`--mingw=`参数用来指定mingw的工具链根目录,其效果跟`--sdk=`是一样的,但是它可以作为全局配置被设置。 ```bash $ xmake g --mingw=/home/mingwsdk $ xmake f -p mingw $ xmake ``` 我们通过`xmake g/global`命令设置`--mingw`根目录到全局配置后,之后每次编译和切换编译平台,就不用额外指定mingw工具链路径了,方便使用。 另外,其他的工具链配置参数用法,跟上文描述的没什么区别,像`--cross`, `--bin=`等都可以根据实际的环境需要,自己控制是否需要额外追加配置来适配自己的mingw工具链。 ### 项目描述设置 #### set\_toolchain 如果觉得每次通过命令行配置比较繁琐,有些配置可以通过在xmake.lua预先配置好,来简化命令配置,比如编译器的指定,就可以通过`set_toolchain`来对每个target单独设置。 ```lua target("test") set_kind("binary") set_toolchain("cxx", "clang") set_toolchain("ld", "clang++") ``` 强制test目标的编译器和链接器使用clang编译器,或者指定交叉编译工具链中的编译器名或者路径。 #### set\_config 我们也可以通过`set_config`来设置在`xmake f/config`命令中的每个配置参数的默认值,这是个全局api,对每个target都会生效。 ```lua set_config("cflags", "-DTEST") set_config("sdk", "/home/xxx/tooksdk") set_config("cc", "gcc") set_config("ld", "g++") ``` 不过,我们还是可以通过`xmake f --name=value`的方式,去修改xmake.lua中的默认配置。 ### 自定义编译平台 如果某个交叉工具链编译后目标程序有对应的平台需要指定,并且需要在xmake.lua里面根据不同的交叉编译平台,还需要配置一些额外的编译参数,那么上文的`-p cross`设置就不能满足需求了。 其实,`-p/--plat=`参数也可以设置为其他自定义的值,只需要跟`is_plat`保持对应关系就可以,所有非内置平台名,都会默认采用交叉编译模式,例如: ```bash $ xmake f -p myplat --sdk=/usr/local/arm-xxx-gcc/ $ xmake ``` 我们传入了myplat自定义平台名,作为当前交叉工具链的编译平台,然后xmake.lua里面我们对这个平台,配置下对应的设置: ```lua if is_plat("myplat") then add_defines("TEST") end ``` 通过这种方式,xmake就可以很方便的扩展处理各种编译平台,用户可以自己扩展支持freebsd, netbsd, sunos等其他各种平台的交叉编译。 我摘录一段之前移植libuv写的交叉编译的配置,直观感受下: ```lua -- for gragonfly/freebsd/netbsd/openbsd platform if is_plat("gragonfly", "freebsd", "netbsd", "openbsd") then add_files("src/unix/bsd-ifaddrs.c") add_files("src/unix/freebsd.c") add_files("src/unix/kqueue.c") add_files("src/unix/posix-hrtime.c") add_headerfiles("(include/uv-bsd.h)") end -- for sunos platform if is_plat("sunos") then add_files("src/unix/no-proctitle.c") add_files("src/unix/sunos.c") add_defines("__EXTENSIONS_", "_XOPEN_SOURCE=600") add_headerfiles("(include/uv-sunos.h)") end ``` 然后,我们就可以切换这些平台来编译: ```bash $ xmake f -p [gragonfly|freebsd|netbsd|openbsd|sunos] --sdk=/home/arm-xxx-gcc/ $ xmake ``` 另外,内置的linux平台也是支持交叉编译的哦,如果不想配置其他平台名,统一作为linux平台来交叉编译,也是可以的。 ```bash $ xmake f -p linux --sdk=/usr/local/arm-xxx-gcc/ $ xmake ``` 只要设置了`--sdk=`等参数,就会启用linux平台的交叉编译模式。 --- --- url: /zh/posts/build-project-so-simply.md --- ## 前言 在开发[xmake](https://github.com/xmake-io/xmake)之前,我一直在使用gnumake/makefile来维护个人C/C++项目,一开始还好,然而等项目越来越庞大后,维护起来就非常吃力了,后续也用过一阵子automake系列工具,并不是很好用。 由于C/C++程序的构建过程比较繁琐,如果不借助IDE工具,很难快速构建一个新的C/C++程序,想要跨平台构建就更加麻烦了。 虽然IDE很好用,也很强大,但是还是有很多不足的地方,例如: * 跨平台开发支持不完善 * 自身环境不一定跨平台 * 过于臃肿 * 不利于服务端自动化部署构建 * 不够灵活,定制化配置构建过程有局限性 当然如果你熟悉makefile的话,也可以手敲makefile,不过不同平台用的make也不相同,比如: gnumake, nmake等,导致makefile语法存在差异性,无法做到一致性编译,而且对开发者有一定的使用门槛。 在win上使用gnumake还得装cygwin,mingw-msys等环境,也非常麻烦,折腾完环境就得半天时间。 目前已经有了很多现代化的构建工具,方便开发者构建和维护C/C++项目,例如:cmake, scons, premake, bazel, gn, gyp等等。 其中很多只能生成对应的IDE工程,然后再通过对应IDE来维护和构建,这种只是解决了C/C++项目的一致性维护问题,但是构建方式不一致,因此还是没解决之前列举的大部分不足点,也无法直接快速构建。 而cmake, scons虽然很强大,但是cmake语法怪异不直观,本人实在是不习惯,scons使用还需要依赖python,py2/py3的问题折腾起来也比较蛋疼。 鉴于此,我采用了lua来描述工程,利用lua的轻量,简洁,灵活,跨平台等特性,来解决上述遇到的各种问题,使用xmake将会带来不一样的构建体验: * 轻量,跨平台,无依赖,无需额外安装python等第三方环境,直接内置lua运行时,一个安装包(或者命令)直接搞定 * 工程描述直观简洁,更符合用户正常的思维习惯 * 支持直接构建,强大的命令行工具,终端用户的福音,装逼用户必备 * vscode, idea, clion, sublime, vim等编辑器插件支持 * 智能检测支持,简化用户编译配置过程 * 插件支持,灵活的用户可扩展性 * vcproj等IDE项目文件生成也支持的哦 * 更多隐藏特性等你来体验 ![xmake-compilation](/assets/img/posts/xmake/xmake-compilation.png) ## 快速上手 不会写makefile?没关系,直接在源码目录运行以下命令即可直接编译: ```bash xmake ``` xmake会自动扫描在当前目录下的源码结构,生成一个`xmake.lua`工程描述文件,然后尝试直接编译。 想要直接运行编译后的可执行程序,简单,直接敲: ```bash $ xmake run ``` 更多相关信息,请参考文章: [xmake新增智能代码扫描编译模式,无需手写任何make文件](http://tboox.org/cn/2017/01/07/build-without-makefile/) ## 快速入门 如果想要更进一步描述工程,调整源码结构,添加一些编译选项什么的,还是需要维护一个名叫xmake.lua的工程描述文件,类似makefile, cmakelist.txt,但是其语法和api经过不断地改进简化,已经相当易用。 最简单的描述例子只需要三行: ```lua target("test") set_kind("binary") add_files("src/*.c") ``` 就可以构建一个可执行程序,编译所有在src目录下的c源文件。 然后直接执行xmake即可编译。 `add_files()`支持通配符文件模式匹配,并且支持`.c, .cpp, .go, .d, .m, .mm, .S, .swift, .rc, .rs`等各种native语言的代码文件,大部分都能支持混编。 我们甚至可以添加.a和.o, .obj文件到`add_files()`,例如: ```lua target("test") set_kind("static") add_files("src/*.c") add_files("lib/libxxx.a", "obj/bbb.o") ``` 上述描述会编译生成一个libtest.a库,在编译归档的时候,会自动将libxxx.a库反解出来,合并到libtest.a中去,并且同时将bbb.o也加进去。 xmake提供的`add_files`是非常强大的,我们还可以再添加一批文件的同时,指定排除某些文件,例如: ```lua add_files("src/**.cpp|test.cpp|arm/*.cpp") ``` 上述描述,在递归添加源文件的同时,排除掉了test.cpp以及arm目录下的源文件。 更多`add_files`用法,请参考文档:[add\_files接口使用文档](https://xmake.io/zh/) ## 使用演示 命令行下的使用过程,大家可以通过一个视频直观的体验下: ## 创建工程 更加省事的方式就是通过上节所说傻瓜式操作方式,自动生成一个xmake.lua,然后在这基础下修修改改就行了。 当然如果没有现成源码,想从新工程创建开始编译,那么可以使用xmake提供的工程模板进行创建: ```bash $ xmake create test ``` 默认创建一个名为test的c可执行项目,源码结构如下: ``` . ├── src │   └── main.c └── xmake.lua ``` 当然你也可以选择语言和模板类型: ```bash $ xmake create -l c++ -t shared test ``` 上述命令创建了一个c++动态库项目,就这么简单。 ## 运行和调试 编译完的可执行程序,直接敲`xmake run`就能运行,xmake会自动找到对应的target目标文件,你也可以传递参数给程序。 如果有多个target目标,你可以指定需要运行的target名,例如: ```bash $ xmake run test ``` 想要快速调试程序?加上`-d`参数即可 ```bash $ xmake run -d test ``` xmake默认会去找系统自带的调试器,然后加载运行,windows上使用vsjitdebugger,linux上gdb,macos上lldb,当然你也可以随意切换到其他调试器。 配合debug模式编译,就能做到使用xmake进行源码调试。 ## 可视化配置和构建 xmake提倡使用命令行的方式来操作,用习惯后效率非常高,而且在windows上,即使没有cygwin,也可以直接在cmd下正常运行。 当然,并不是所有用户习惯命令行,因此xmake也提供了编辑器插件,与各大编辑器进行集成,例如: #### xmake-vscode插件 #### xmake-idea插件 #### xmake-sublime插件 #### xmake-tui界面 除了编辑器插件,xmake甚至自己封装实现了一整套跨平台tui字符界面库,然后仿kconfig/menuconf的界面风格,实现了一个类似的可视化字符界面菜单配置。 这个不需要额外的插件,只需要在终端下执行: ```bash $ xmake f --menu ``` 就可以显示菜单配置界面进行编译配置,配置完即可根据当前配置进行编译,效果如下: ## 定制化编译 想要更加灵活的编译配置?那就得要修改xmake.lua啦,不过还是很简单的。 #### 添加编译选项 ```lua target("test") set_kind("binary") add_files("src/*.c") if is_mode("debug") then add_cxflags("-DDEBUG") end ``` 上面代码中,`add_cxflags`接口就是同时配置C/C++代码的编译选项,并且只在debug模式下生效,也就是执行下面命令的时候: ```bash $ xmake f -m debug $ xmake ``` #### 使用内置选项 像添加宏定义,设置警告级别,优化级别,头文件搜索目录什么的,完全没必要使用原始的`add_cxflags`接口,xmake有提供更加方便的接口,更加智能化的处理来简化配置,也更加通用跨平台,例如: ```lua add_defines("DEBUG") set_optimize("fast") set_warnings("all", "error") target("test") set_kind("binary") add_files("src/*.c") target("test2") set_kind("binary") add_files("src2/*.c") ``` 跟刚才的配置不同的是,此处设置放在了target的上面,此处不属于target域,是root全局设置,会影响下面的所有target目标程序的编译设置,这样可以简化配置,避免冗余。 ## 灵活的脚本控制 对于高端用户,构建需求复杂多变,xmake也提供了对应解决方案,各个构建阶段都可以灵活定制: ```lua target("test") set_kind("binary") add_files("src/*.c") after_build(function (target) os.exec("file %s", target:targetfile()) end) ``` 上述代码在编译程序结束后,执行file命令查看目标程序相关信息,目前xmake可以在build, clean, run, install, uninstall等各个阶段的前后插入自定义的脚本,也可以直接内置action,例如: on\_install会覆盖内置的安装逻辑,提供给用户足够的灵活性。 ## 方便的多目标依赖 很多时候,一个项目会有多个target目标程序,之间存在依赖关系,例如: 一个可执行程序hello,依赖一个静态库libtest.a,我们只需要通过add\_deps将两个target做个关联就行了,libtest.a的搜索目录,头文件目录设置什么的都不需要关心,xmake会自动处理: ```lua target("test") set_kind("static") add_files("src/test/*.c") target("hello") add_deps("test") --添加依赖 set_kind("binary") add_files("src/hello/*.c") ``` ## 预编译头文件支持 xmake支持通过预编译头文件去加速c/c++程序编译,目前支持的编译器有:gcc, clang和msvc。 ```lua target("test") -- ... set_pcxxheader("header.h") ``` 各大编译器对预编译头的处理方式存在很大差异,而xmake将其差异性隐藏了起来,提供一致性的描述设置,简化用户在跨平台编译时候的处理, 具体关于编译器对预编译头文件的处理,可参考相关文章:[不同编译器对预编译头文件的处理](http://tboox.org/cn/2017/07/31/precompiled-header/) ## 自定义编译规则 xmake不仅原生内置支持多种语言文件的构建,而且还可以通过自定义构建规则,让用户自己来实现复杂的未知文件构建。 我们可以通过预先设置规则支持的文件后缀,来扩展其他文件的构建支持: ```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") ``` 我们也可以指定某些零散的其他文件作为markdown规则来处理: ```lua target("test") -- ... add_files("src/test/*.md.in", {rule = "markdown"}) ``` 注:通过`add_files("*.md", {rule = "markdown"})`方式指定的规则,优先级高于`add_rules("markdown")`设置的规则。 ## IDE工程文件生成 xmake提供了丰富的插件扩展,其中vcproj, makefile等工程文件的生成就是作为插件提供,使用起来也非常简单: ```bash $ xmake project -k vs2017 -m "debug,release" ``` 即可生成带有debug, release两种编译模式的vc工程,同时支持x86和x64。 生成的工程目录结构会根据添加的所有源文件的目录结构,自动分析生成直观的文件树,方便vs去浏览查看。 makefile的生成如下: ```bash $ xmake project -k makefile ``` 后续会陆续更多其他工程文件,也欢迎大家来贡献哦。 ## 灵活简单的插件扩展 上节的IDE工程文件生成,在xmake中就是作为插件来提供,这样更加方便扩展,也能让用户快速定制自己的插件,只需要定义个task插件任务就行了: ```lua -- 定义一个名叫hello的插件任务 task("hello") -- 设置类型为插件 set_category("plugin") -- 插件运行的入口 on_run(function () print("hello xmake!") end) -- 设置插件的命令行选项,这里没有任何参数选项,仅仅显示插件描述 set_menu { -- usage usage = "xmake hello [options]" -- description , description = "Hello xmake!" -- options , options = {} } ``` 上述代码就是一个最为简单的`hello xmake!`插件,运行`$xmake hello`就可看到执行输出,`set_menu`用于配置插件命令行选项,这个不设置就是内部task,无法在命令行下调用。 更加详细的插件说明以及内置插件列表可参考文档:[插件手册](https://xmake.io/zh/) ## 查找依赖包 xmake参考了cmake对于`find_*`系列接口的设计,实现在项目中动态的查找和添加包依赖。 ```lua target("test") set_kind("binary") add_files("*.c") on_load(function (target) import("lib.detect.find_package") target:add(find_package("zlib")) end) ``` 上述描述代码,通过`lib.detect.find_package`来查找包,如果找到zlib包,则将links, includedirs和linkdirs等信息添加到target中去。 ## 交互式命令执行(REPL) 有时候在交互模式下,运行命令更加的方便测试和验证一些模块和api,也更加的灵活,不需要再去额外写一个脚本文件来加载,不过我一般用来做计算器用用(好吧。。) ```bash # 不带任何参数执行,就可以进入 $ xmake lua > # 进行表达式计算 > 1 + 2 3 # 赋值和打印变量值 > a = 1 > a 1 # 多行输入和执行 > for _, v in pairs({1, 2, 3}) do >> print(v) >> end 1 2 3 ``` 我们也能够通过 import 来导入扩展模块: ```bash > task = import("core.project.task") > task.run("hello") hello xmake! ``` ## 编译环境支持 当前xmake的最新版本已经支持很多sdk环境的集成编译,例如: * \[x] Visual Studio编译环境 * \[x] mingw编译环境 * \[x] cygwin编译环境 * \[x] Android NDK编译环境 * \[x] Xcode编译环境(支持iPhoneos/Macosx构建) * \[x] 系统gcc/clang编译环境 * \[x] 交叉工具链编译环境 * \[x] Cuda编译环境 * \[ ] Qt编译环境(正在支持中) * \[ ] Windows WDK编译环境(正在支持中) ## FAQ #### xmake有哪些用途? 1. 跨平台维护和编译C/C++项目 2. CI上部署自动化构建 3. 开源代码的快速移植 4. 临时的测试代码编写和快速运行 5. 与自己喜欢的编辑器集成,打造属于自己的C/C++开发环境 6. 与其他native语言的混合编译 7. 嵌入式开发下的交叉编译 8. 提升逼格 对于第三点的用途,我平常用的最多,因为我经常需要移植第三方的开源项目,它们使用的构建工具各不相同,有automake,cmake等等,其支持的构建平台力度也都不相同,经常会遇到需要的平台不支持的问题。 没办法,只好自己敲makefile来移植代码,然后适配自己需要支持的那些平台,还有交叉工具链,很蛋疼,自从写了xmake后,我现在平常移植代码方便了很多,效率提升非常明显。 #### 怎样看实时编译警告信息? 为了避免刷屏,在构建时候,默认是不实时输出警告信息的,如果想要看的话可以加上`-w`选项启用编译警告输出就行了。 ```bash $ xmake [-w|--warning] ``` #### 怎样看详细的编译参数信息? 请加上 `-v` 或者 `--verbose` 选项重新执行xmake后,获取更加详细的输出信息 例如: ```sh $ xmake [-v|--verbose] ``` 如果加上 `--backtrace` 选项也可以获取出错时的xmake的调试栈信息 ```bash $ xmake -v --backtrace ``` ![xmake-verbose](/assets/img/posts/xmake/xmake-verbose.png) ## 快速安装 最后我们讲下,如何安装xmake,通常只需要一个脚本命令就能搞定。 #### 一键安装脚本 ```bash bash <(curl -fsSL https://raw.githubusercontent.com/tboox/xmake/master/scripts/get.sh) ``` #### windows安装包 对于windows用户,提供了安装包来快速安装,可到[Github Releases](https://github.com/xmake-io/xmake/releases)上下载对应版本。 更加详细的安装过程,见相关文档: [安装说明](https://xmake.io/zh/) ## 结语 xmake还有很多非常有用的特性,例如:编译器特性检测、丰富的模块库、依赖包管理、自定义选项等等,一篇文章讲不完这么多,大家有兴趣的话,可以去[官方文档](https://xmake.io/zh/)里面看看,还有很多隐藏特性等着你哦。 --- --- url: /zh/posts/variables-usage.md --- title: xmake内建变量和外置变量的使用 tags: \[xmake, 内建变量, 外置变量] date: 2016-08-08 author: Ruki *** ## 内建变量 内置在字符串中,例如: ```lua set_objectdir("$(buildir)/.objs") ``` 其中的$(buildir)就是内建变量,这些是随着每次xmake config的配置改变而自动改变的。 目前支持的一些变量如下: * $(buildir): 编译输出目录,可通过:`xmake f -o /tmp` 修改 * $(projectdir): 工程主目录,可通过:`xmake f -P ./project` 修改 * $(os): 编译目标的操作系统 * $(plat): 编译目标的所在的平台,可通过:`xmake f -p android`修改 * $(mode): 编译模式:debug、release、profile,可通过: `xmake f -m debug` 修改 * $(arch): 编译目标的架构,可通过: `xmake f -a armv7` 修改 注:所有通过`xmake f/config`配置的参数选项都可以通过内置变量访问,例如android下: ```lua xmake f -p android --ndk=/xxxx ``` 那么$(ndk)就是可访问变量,并且随着配置的改变而改变,但是这个在非android平台不能使用。 其他所有的配置相关变量,可以通过以下命令来查看: ```lua xmake f --help ``` ## 外置变量 外置变量很简单,就是lua的变量操作,因为xmake.lua本身就是lua脚本,那么lua的所有特性当然都能直接使用,因此可以这么使用: ```lua local root = "/tmp" set_objectdir(root .. ".objs") ``` 通过lua的字符串变量追加语法就行了,是不是很简单。 --- --- url: /zh/posts/v2.1.1-goal.md --- title: xmake后期发展随想 tags: \[xmake, 包管理, 插件, 代码移植编译] date: 2016-06-25 author: Ruki *** 随着xmake v2.0.1 版本的发布,这大半年的辛苦总算告一段落,这个版本我基本上重构整个项目的90%的代码,几乎算是重写了,但结果还算挺满意的。。 因为上个版本的架构设计的不是很好,不能很好进行扩展,也不支持插件模式,语法设计上也不严谨,容易出现各种隐患,这对于后期维护和发展来说,已经出现了不可逾越的瓶颈。。 每个项目到了一定阶段,都是要不断重构,重新构思整体架构,才能使得项目不断的向好的方向演进。。 (当然如果是公司项目就另当别论了,坑太多,历史负担较重,不是说要重构就能让你重构的。=。=) 回归正题,目前xmake基本上所有模块都是可扩展的: * 插件扩展 * 工程模板扩展 * 平台架构扩展 * action扩展 * option选项扩展 * 自定义task任务机制 * 宏脚本扩展 模块化和可扩展性,使得xmake整体是高度解耦的,整个core的内核算法实现非常轻量,其他模块如果我们想要扩展它,只需要把自己实现的脚本放到对应目录,就可以实现自注册,自加载。。 并且每个插件模块内部都有严格的作用域控制、沙盒化处理,非常安全,不会干扰到其他插件。。 下一个大版本,我打算开始研究下,怎么去实现完善的依赖包管理,目前的一些想法和构思: * 自动检测依赖包,如果存在直接链接编译,如果不存在,从远程仓库中自动下载对应版本,进行本地编译安装,然后自动集成和链接 * 支持多架构、多平台以及交叉平台的包管理 * 参考homebrew的包管理思想,将仓库放在项目中,通过git维护 * 为了实现交叉平台的包管理,仓库的包描述,除了提供包原代码的url外,还提供移植描述脚本 可能我说的有点模糊,先说说现有的一些包管理工具,例如:homebrew、apt-get、pacman等等。。 大同小异,都是下载、自动编译、安装集成到系统中,不过都只能支持pc原有的主机平台,并不支持交叉平台 例如:在windows上我要自动加载安装一个ios armv7s的包,集成到我的项目中。。这就不行了。 而xmake的下个版本,打算做的就是这个,简单的说: `我要做一个移植仓库,实现一人移植,万人使用` 以后,如果用xmake编译项目,这个项目中说需要链接 android 版本 armv7-a 的 libpng.a,那么xmake 就会先检测本地仓库是否存在,不存在的话,就会从移植仓库中,check处移植脚本,自动进行本地移植编译,然后链接到这个项目中去。。。 明白了吗,是不是很有趣。。? 现在的开源项目越来越多,平台也越来越多,但是很多c/c++项目的移植工作相当麻烦,不同项目编译方式区别很大,平台支持力度也各不一样。。 而我们平常移植后,基本上只能自己使用,没法分享给别人 而下个版本,xmake要做的就是让其他人不用重新再移植一边,只要有人移植过,把移植过程记录成移植脚本,push到xmake的移植仓库中,让所有人共享移植成果。。这是多美妙的一件事哈。。:) 我表达能力有限,貌似有点啰嗦了,最后我对xmake的期望就是: `它不仅仅是个跨平台构建工具,也将会成为移植神器,一人移植,万人共享就是xmake的目标!` --- --- url: /zh/posts/project-description.md --- xmake的工程描述文件,摈弃了makefile的繁琐复杂,借鉴了premake的简洁明了,原生支持lua脚本,使得更加的灵活、方便扩展。 工程默认描述文件名为xmake.lua,支持多级目录嵌套,也可以通过以下命令,指定其他文件作为工程描述文件: ```bash xmake -f /tmp/xxx.lua xmake --file=xxx.lua ``` 下面先来看一个最简单的例子: ```lua -- 添加一个名为demo的目标到工程 target("demo") -- 设置目标程序类型为二进制可执行程序,一般为console的终端命令行程序 set_kind("binary") -- 添加src目录下的所有c文件 add_files("src/*.c") ``` 怎么样简单吧,这样就已经完成了一个最简单的工程描述。。 下面我们看一个稍微复杂一点的例子,这个例子中对release、debug模式进行了不同的设置: ```lua -- 如果当前编译的是debug模式 if is_mode("debug") then -- 启用调试符号 set_symbols("debug") -- 禁用优化 set_optimize("none") end -- 如果当前编译的是release模式 if is_mode("release") then -- 设置符号可见性为不可见 set_symbols("hidden") -- 启用最快优化模式 set_optimize("fastest") -- 去除所有符号信息,包括调试符号 set_strip("all") end -- 添加一个名为test的目标 target("test") -- 将test编译成为静态库类型 set_kind("static") -- 添加所有c++文件,包括子目录(注:**表明多级递归匹配模式) add_files("src/**.cpp") ``` 其实也不是很复杂吧,由于采用lua语法,所以逻辑上更加的灵活,你完全可以用lua的分支、循环、函数等语法,进行更加灵活的配置。。 --- --- url: /zh/posts/condition-and-select-compile.md --- xmake 提供了一些内置的条件判断api,用于在选择性编译时,获取到一些工程状态的相关信息,来调整编译逻辑。。 例如:`is_os`, `is_plat`, `is_arch`, `is_kind`, `is_mode`, `is_option` ### `is_mode` 我们先拿最常用的`is_mode`来讲讲如何使用,这个api主要用来判断当前的编译模式,例如平常编译配置的时候,会执行: ```bash $ xmake f -m debug $ xmake ``` 来编译`debug`版本,那么模式就是`debug`,那么`release`版本,也就是`release`了 ```bash $ xmake f -m release $ xmake ``` 但是如果仅仅只是这么配置,xmake还是不知道如果为debug进行编译,如何编译release版本,因为这些模式的值不是内置的 我们可以随便设置,例如:profile, checking等等,用来编译性能模式,检测模式,这些就看咱们项目实际的需求了。。 一般情况下只需要`debug`和`release`就行了,那如何区分呢,这就需要在`xmake.lua`进行配置了,一般可参考如下配置: ```lua -- 如果当前编译模式是debug if is_mode("debug") then -- 添加DEBUG编译宏 add_defines("DEBUG") -- 启用调试符号 set_symbols("debug") -- 禁用优化 set_optimize("none") -- 如果是release模式 elseif is_mode("release") then -- 隐藏符号 set_symbols("hidden") -- strip所有符号 set_strip("all") -- 开启优化为:最快速度模式 set_optimize("fastest") -- 忽略帧指针 add_cxflags("-fomit-frame-pointer") add_mxflags("-fomit-frame-pointer") end ``` 通过判断是否在编译debug版本,来启用和禁用调试符号信息,并且判断是否禁用和启用优化。 当然,如果我们的项目还设置了其他模式,例如性能分析模式:profile,那么还可以通过这个来判断是否需要添加一些分析分析上的编译选项。 ### `is_plat` 接下来我们讲讲这个编译平台的判断,这个也非常实用哦,虽然我们的工具是为了跨平台开发,通常的配置肯定都是通用的 但是毕竟项目成千上万,需求各不相同,总归会有些项目需要针对不同的平台做些编译上的特殊处理 这个时候,我们就需要这个api了,例如: ```lua -- 如果当前平台是android if is_plat("android") then add_files("src/xxx/*.c") end --如果当前平台是macosx或者iphoneos if is_plat("macosx", "iphoneos") then add_mxflags("-framework Foundation") add_ldflags("-framework Foundation") end ``` 这里针对android平台,增加了一些特殊代码的编译,针对macosx和iphoneos平台,增加了Foundation框架的链接。 这里还有个比较实用的小技巧,`is_xxx`系列接口,都是可以同时传递多个参数的,逻辑上是or的关系 我们可以像上面那么写法: ```lua if is_plat("macosx", "iphoneos", "android", "linux") then end ``` 否则如果用lua的原生语法的话,虽然也可以,但是会很臃肿,例如: ```lua if is_plat("macosx") or is_plat("iphoneos") or is_plat("android") or is_plat("linux") then end ``` 除了`is_xxx`系列,像:`add_xxxs` 这种后缀有`s`的复数api,都是可以传递多个参数的哦,例如`add_files`: ```lua add_files("src/*.c", "test.c", "hello.cpp") ``` 等等,这里就不一一介绍了。。。 ### `is_arch` 这个跟`is_plat`类似,不过是用来判断当前编译的目标架构的,也就是: ```bash xmake f --arch=x86_64 ``` 然后,我们在工程描述中,进行判断: ```lua -- 如果当前架构是x86_64或者i386 if is_arch("x86_64", "i386") then add_files("src/xxx/*.c") end --如果当前平台是armv7, arm64, armv7s, armv7-a if is_arch("armv7", "arm64", "armv7s", "armv7-a") then -- ... end ``` 如果像上面那样一个个去判断所有arm架构,也许会很繁琐,毕竟每个平台的架构类型很多,xmake提供了类似`add_files`中的通配符匹配模式,来更加简洁的进行判断: ```lua --如果当前平台是arm平台 if is_arch("arm*") then -- ... end ``` 用\*就可以匹配所有了。。 ### `is_os` 这个很简单,用来判断当前编译目标,例如: ```lua -- 如果当前操作系统是ios if is_os("ios") then add_files("src/xxx/*.m") end ``` 目前支持的操作系统有:windows、linux、android、macosx、ios ### `is_kind` 用来判断当前是否编译的是动态库还是静态库 一般用于如下场景: ```lua target("test") -- 通过配置设置目标的kind set_kind("$(kind)") add_files("src/*c") -- 如果当前编译的是静态库,那么添加指定文件 if is_kind("static") then add_files("src/xxx.c") end ``` 编译配置的时候,可手动切换,编译类型: ```lua -- 编译静态库 xmake f -k static xmake -- 编译动态库 xmake f -k shared xmake ``` ### `is_option` 如果某个自动检测选项、手动设置选项被启用,那么可以通过`is_option`接口来判断,例如: ```lua -- 如果手动启用了xmake f --demo=y 选项 if is_option("demo") then -- 编译demo目录下的代码 add_subdirs("src/demo") end ``` --- --- url: /zh/posts/plugin-doxygen.md --- 这个doxygen插件比较简单,说白了就是一键生成工程文档,只需要执行下面这行命令就行了 ```bash xmake doxygen ``` 当然你也可以指定输出目录,可以工程源码目录: ```bash xmake doxygen -o /tmp/output project/src ``` 生成的文档中,工程名和版本号,就是xmake.lua中通过如下两条api设置的: ```lua -- 设置工程名 set_project("tbox") -- 设置版本号 set_version("v1.5.1") ``` 这个插件执行的时候回去检测当前平台是否存在doxygen工具,如果没有的话,是没法生成文档的哦。。: ) --- --- url: /zh/posts/plugin-modules.md --- xmake通过import接口,可以在自定义脚本中导入各种内置类库和扩展类库模块,使得xmake的插件开发具有更多的灵活性,提供更丰富的功能。 我们先看下,目前xmake提供的一些类库: ``` . ├── _g.lua ├── assert.lua ├── catch.lua ├── coroutine.lua ├── debug.lua ├── finally.lua ├── format.lua ├── ifelse.lua ├── import │   └── core │   ├── base │   │   └── option.lua │   ├── platform │   │   ├── environment.lua │   │   ├── menu.lua │   │   └── platform.lua │   ├── project │   │   ├── cache.lua │   │   ├── config.lua │   │   ├── global.lua │   │   ├── history.lua │   │   ├── menu.lua │   │   ├── package.lua │   │   ├── project.lua │   │   ├── target.lua │   │   ├── task.lua │   │   └── template.lua │   └── tool │   ├── compiler.lua │   ├── linker.lua │   └── tool.lua ├── import.lua ├── inherit.lua ├── insert.lua ├── io.lua ├── ipairs.lua ├── math.lua ├── os.lua ├── pairs.lua ├── path.lua ├── print.lua ├── printf.lua ├── raise.lua ├── string.lua ├── table.lua ├── tonumber.lua ├── tostring.lua ├── try.lua ├── utils.lua └── vformat.lua ``` 在根目录下的模块和api都是属于内建的,不需要import也可以直接使用,属于常用api,提供了xmake最基础的特性。。 在子目录下的是扩展模块,需要import后才能使用,导入规则见[import](https://xmake.io/zh/),例如: ```lua import("core.project.task") ``` 需要注意的是:xmake对自定义的脚本采用了异常处理机制,大部分情况下,调用的api是不需要判断返回值状态是否成功,如果出错了,会立即中断,并且显示错误信息 这样语法上更加的精简可读,并且更安全,所有api的输入输出,内部都有检测,状态不对会立即自动报错。 当然如果我们想要自己获取这个异常的状态,做一些逻辑上的处理,可以通过try/catch来实现,使用起来也非常简单。 下面简单介绍下一些常用的内置模块api,这些模块不需要import就可以使用的哦。:) #### os模块 ```lua -- 运行shell命令,如果运行失败直接中断,并显示出错信息,我们不需要判断返回值 os.run("echo hello xmake!") -- 复制文件 os.cp("/tmp/src", "/tmp/dst") -- 删除文件或者目录 os.rm("/tmp/dir") -- 移动文件 os.mv("/tmp/old", "/tmp/new") -- 判断文件是否存在 if os.isfile("/tmp/file") then end -- 判断目录是否存在 if os.isdir("/tmp/dir") then end -- 匹配遍历文件,*为非递归匹配,**为递归匹配 for _, file in ipairs(os.match("src/*.c")) do print(file) end -- 匹配遍历目录,*为非递归匹配,**为递归匹配 for _, file in ipairs(os.match("src/*", true)) do print(file) end ``` #### 常用api ```lua -- 抛出异常,立即中断 raise() -- 抛出异常,立即中断,并抛出异常错误信息 raise("error info") -- 抛出异常,立即中断,并抛出异常错误代码 raise(-1) -- 显示输出并换行,支持格式化输出,跟lua的print稍有不同 print("hello %s", "xmake") -- 显示输出不换行 printf("hello %s", "xmake") -- 格式化字符串 s = format("hello %s", "xmake") ``` #### 异常捕获api ```lua try { -- try块,里面抛出异常 function () raise("error") end, catch { -- catch块,捕获异常 function (errors) print(errors) end } } -- 获取try块的返回值,如果没有异常的话返回true local ok = try { -- try块,里面抛出异常 function () -- may be error return true end } try { -- try块,里面抛出异常 function () raise("error") end, catch { -- catch块,捕获异常 function (errors) print(errors) end }, finally { -- finally 块 function () end } } ``` #### path模块 ```lua -- 获取相对路径 path.relative("/tmp/a") -- 获取绝对路径 path.absolute("src") -- 获取目录 path.directory("/tmp/a") -- 获取文件名 test.c path.filename("/tmp/test.c") -- 获取base名 test path.basename("/tmp/test.c") -- 获取扩展名 path.extension("/tmp/test.c") -- 拼接路径 /tmp/test.c path.join("/tmp", "test.c") ``` #### io 模块 ```lua -- 打开一个写文件 file = io.open("/tmp/a", "w") -- 写文件数据 file:write("hello") -- 写文件格式化行 file:print("hello %s", "xmake") -- 写文件格式化不换行 file:printf("hello %s", "xmake") -- 关闭文件 file:close() -- 序列化写一个lua对象到文件 io.save("/tmp/a", object) -- 反序列化读取一个文件对象 object = io.load("/tmp/a") -- 读取文件数据,并显示 io.cat("/tmp/a") -- 模式替换文件内容, 替换空格字符为 "space" io.gsub("/tmp/a", "%s", "space") ``` 还有一些是lua的常用模块,这里就不多说了,例如:`string, table, debug, coroutine, pairs, ipairs, tostring, tonumber` 等等 --- --- url: /zh/posts/generate-vs2008-project.md --- xmake master上最新版本已经支持vs2008工程文件的生成,通过`project`插件的方式提供,例如: 创建vs2008工程文件: ```bash $ xmake project -k vs2008 ``` 默认输出目录是在当前工程的下面,会生成一个vs2008的工程文件夹,打开解决方案编译后,默认的输出文件路径跟xmake.lua描述的是完全一致的,一般都是在build目录下 除非你手动指定其他的构建目录,例如:`xmake f -o /tmp/build` 创建vs2008工程文件,并且创建工程文件到指定目录: ```bash $ xmake project -k vs2008 f:\vsproject ``` 目前这个插件也是刚刚跑通,身边暂时没有其他vs版本可供测试,理论上已经可以支持vs200x的所有版本了(vs2002, vs2003, vs2005, vs2008) 如果有兴趣的同学可以先行测试下其他版本。 另外目前vs2010以上版本,暂时还不支持,后续也会陆续实现掉,想要用vs2015来编译的话,理论上vs是可以向下兼容支持低版本vs工程的,可以尝试用vs2015加载vs2008的工程文件试试。。 后面的工作计划: * 优化现有vs200x工程文件,目前的生成方式不利于vs编译优化,所以编译速度较慢 * 实现vs2010以上版本的生成插件 --- --- url: /zh/posts/safer-install-and-uninstall.md --- 最近对xmake的操作权限进行了升级,提供更加安全的命令操作,例如: 1. 改进`xmake install`和`xmake uninstall`命令,提供更加安全地安装和卸载支持 2. 参考homebrew,禁止在root下运行xmake命令 3. 改进xmake自身的编译安装脚本,不在root下进行build ##### 安全问题1 之前的`xmake install`和`xmake uninstall`行为,是自动`build`后进行安装,而大部分情况下安装目录是在`/usr/local`目录下。 因此,需要root权限才能写入,那么之前的方式只能暴力地直接加上`sudo xmake install`来执行。 可想而知,虽然安装确实成功了,但是由于默认的自动构建行为,导致生成的临时`*.o`, `*.a`等各种文件都具备了root权限,而且一个不小心就会覆盖系统的一些库文件。 因此,为了避免这个问题,xmake改进了安装逻辑,将`build`和`install`分离成两个独立的阶段,分别使用不同的权限,并且提供更加友好的提示信息,例如: ![safer\_installation](/assets/img/posts/xmake/safer_installation.png) ##### 安全问题2 这个主要参考了homebrew对`sudo brew`的处理,为了提高安全性,它禁止在root下运行命令,其实很多其他工具都有这个特性,`CocoaPods`也是如此。 那它是怎么实现的呢,我翻了下homebrew的源码,在`brew.sh`中有这么一段脚本: ```ruby check-run-command-as-root() { [[ "$(id -u)" = 0 ]] || return # Homebrew Services may need `sudo` for system-wide daemons. [[ "$HOMEBREW_COMMAND" = "services" ]] && return # It's fine to run this as root as it's not changing anything. [[ "$HOMEBREW_COMMAND" = "--prefix" ]] && return odie < "0x0A000000" then target:add("defines", "TEST") end end) ``` 上述代码通过判断WIN32\_WINNT的版本值,来定制添加一些相关配置,这个版本值会根据`wdk.env.winver`的配置自动适配更新,目前提供的这些内置版本值还有: ``` target:values("wdk.env.winnt_version"): WIN32_WINNT target:values("wdk.env.ntddi_version"): NTDDI_VERSION target:values("wdk.env.winver_version"): WINVER ``` 关于更多xmake下WDK开发相关介绍,请参考文档:[WDK驱动程序开发](https://xmake.io/zh/) --- --- url: /zh/posts/simplify-xmake-description.md --- [xmake](https://github.com/xmake-io/xmake)的初衷就是为了让用户能够用最简单直接的方式去描述工程,提供跨平台项目构建,因此,`简洁,灵活` 是xmake.lua的核心设计思想。 通过之前的那篇文章:[xmake入门,构建项目原来可以如此简单](http://tboox.org/cn/2018/03/26/build-project-so-simply/),我们对如何使用xmake去构建项目有了大概的了解,并且能够编写一些简单的xmake.lua去描述项目,例如: ```lua target("test") set_kind("binary") add_files("src/*.c") ``` 但是平常我们实际的项目维护,不可能这么简单,会有各种各样的配置需求,例如: 添加每个平台特有的flags,处理debug/release编译模式,多个target的依赖编译等等,在混杂了用户各种配置需求后,xmake.lua的维护很容易变得很臃肿,可读性变差。 因此本文会介绍一些常用的编写模式,去尽可能的利用xmake的设计特性,去简化对工程的描述,提高可读性和易维护性,避免用户因为不了解xmake而导致的一些错误写法。 ## 在根域添加通用配置 xmake的配置关系是根据tree结构继承,子xmake.lua会集成父xmake.lua中的配置,同一个xmake.lua中,所有target的配置会集成根作用域的配置,因此一些通用配置,可以放置在根域,避免重复设置,例如: #### 简化前 ```lua target("test1") set_kind("binary") add_files("src1/*.c") if is_mode("debug") then set_symbols("debug") set_optimize("none") end if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end if is_plat("linux") then add_defines("PLAT_IS_LINUX") end if is_plat("macosx") then add_defines("PLAT_IS_MACOSX") end if is_plat("android") then add_defines("PLAT_IS_ANDROID") end target("test2") set_kind("binary") add_files("src2/*.c") if is_mode("debug") then set_symbols("debug") set_optimize("none") end if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end if is_plat("linux") then add_defines("PLAT_IS_LINUX") end if is_plat("macosx") then add_defines("PLAT_IS_MACOSX") end if is_plat("android") then add_defines("PLAT_IS_ANDROID") end ``` #### 简化后 ```lua if is_mode("debug") then set_symbols("debug") set_optimize("none") end if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end if is_plat("linux") then add_defines("PLAT_IS_LINUX") end if is_plat("macosx") then add_defines("PLAT_IS_MACOSX") end if is_plat("android") then add_defines("PLAT_IS_ANDROID") end target("test1") set_kind("binary") add_files("src1/*.c") target("test2") set_kind("binary") add_files("src2/*.c") ``` 把通用的设置放到根域后,可以避免对每个target的重复设置,target越多,效果越明显。 ## 利用rule去简化常用配置 对于一些常用的配置,xmake最新版本中提供了内置规则去简化它,例如: `mode.debug`, `mode.release`规则等,提供对编译模式的内置配置处理,我们还是以刚才的代码为例,看看应用规则后的效果: ```lua add_rules("mode.debug", "mode.release") if is_plat("linux") then add_defines("PLAT_IS_LINUX") end if is_plat("macosx") then add_defines("PLAT_IS_MACOSX") end if is_plat("android") then add_defines("PLAT_IS_ANDROID") end target("test1") set_kind("binary") add_files("src1/*.c") target("test2") set_kind("binary") add_files("src2/*.c") ``` 看上去,比刚才的结果更加简化了不少,其中`mode.release`规则被应用后,相当于配置了: ```lua if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end ``` 这其实有点像c/c++中的宏,不过rule更加强大,因为它还可以对一个target同时叠加多个rule,甚至用户可以自定义一些rule去简化自己的常用配置,或者自定义扩展构建规则。 具体对rule的使用说明,我后续会有单独的文章来介绍,如果用户感兴趣的话,可以先看下相关文档深入了解下,里面还有很多xmake提供的内建规则: [构建规则文档](https://xmake.io/zh/) ## 利用内建变量条件配置 上述代码中,其实还是有许多冗余的地方,例如: ```lua if is_plat("linux") then add_defines("PLAT_IS_LINUX") end ``` 这里每次都判断下平台,仅仅只是为了设置一个宏定义的话,没必要这么写,直接使用xmake提供的内建变量`$(plat)`会更加简单直接,例如: ```lua add_defines("PLAT_IS_$(plat)") ``` 不过,这里还不是完全一致,我们需要的是大写的定义,因此可以改成: ```lua add_defines("PLAT_IS_$(plat:upper)") ``` 最后贴下完整的简化代码: ```lua add_rules("mode.debug", "mode.release") add_defines("PLAT_IS_$(plat:upper)") target("test1") set_kind("binary") add_files("src1/*.c") target("test2") set_kind("binary") add_files("src2/*.c") ``` 相比最初的配置,现在已经相当精简了,并且更加可读,易维护。如果想了解更多的内建变量,请参考文档:[内建变量](https://xmake.io/zh/) ## 利用lua脚本简化配置 很多时候,会有一些重复的逻辑配置,并且包含了一些用户的配置逻辑在里面,单纯用内建变量满足不了需求,这个时候就可以通过写一小段lua脚本,或者封装个lua函数去简化它们。 例如,我们想定义好几个target,但是他们的编译选项完全相同,仅仅是源码所在目录不同,就比如之前的代码,我们可以继续简化: ```lua add_rules("mode.debug", "mode.release") add_defines("PLAT_IS_$(plat:upper)") for _, id in ipairs({"1", "2"}) do target("test" .. id) set_kind("binary") add_files("src" .. id .. "/*.c") end ``` 或者通过定义个function来实现: ```lua function define_target(...) for _, id in ipairs({...}) do target("test" .. id) set_kind("binary") add_files("src" .. id .. "/*.c") end end add_rules("mode.debug", "mode.release") add_defines("PLAT_IS_$(plat:upper)") define_target(1, 2) ``` 注:这种方式,不能滥用,对于确实有太多的重复配置的时候,可以通过脚本适当简化下,使用不当也会起一些反效果,反而令整体可读性降低。 ## 利用includes分离单个xmake.lua 上节介绍的方法去简化target的定义,可读性还不是很好,对于单个xmake.lua,如果充斥太多target,有可能会变得非常臃肿,这个时候,我更倾向于使用includes去分离`xmake.lua`,每个子目录提供一个单独的`xmake.lua` 假如有下面的目录结构: ``` ├── src1 │   └── main.c ├── src2 │   └── main.c └── xmake.lua ``` 我们可以在每个src子目录下,单独提供一个子`xmake.lua`去维护: ``` ├── src1 │   ├── main.c │   └── xmake.lua ├── src2 │   ├── main.c │   └── xmake.lua └── xmake.lua ``` 然后根`xmake.lua`中可以通过includes去包含子目录的`xmake.lua`配置,之前的配置代码可以简化成: ```lua add_rules("mode.debug", "mode.release") add_defines("PLAT_IS_$(plat:upper)") includes("src1", "src2") ``` 然后子`xmake.lua`中,仅对当前目录下的源码提供一个对应的target,比如`src1/xmake.lua`的内容如下: ```lua target("test1") set_kind("binary") add_files("*.c") ``` 另外,根`xmake.lua`的配置会自动继承给子配置,不需要再去设置一遍,除非有当前子target特有的配置,那么可以在子`xmake.lua`里面单独设置。 ## 利用文件匹配模式简化源文件配置 `add_files`提供了强大的文件匹配模式去简化源文件的添加,用户不需要每次单独添加一个文件,这样太过繁琐: ```lua target("test") add_files("src/test1.c") add_files("src/test2.c") add_files("src/subdir/test1.c") add_files("src/subdir/test2.c") ``` 通过匹配模式,我们可以递归添加所有相关源文件: ```lua target("test") add_files("src/**.c") ``` 其中通配符\*表示匹配当前目录下文件,而\*\*则匹配多级目录下的文件。我们再贴一些例子代码,直观感受下: ```lua add_files("src/test_*.c") add_files("src/xxx/**.cpp") add_files("src/asm/*.S", "src/objc/**/hello.m") ``` `add_files`不仅可以匹配文件,还有可以在添加文件同时,过滤排除指定模式的一批文件,例如: ```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") ``` 我们也可以通过`del_files`接口,从前面`add_files`接口添加的文件列表中,删除指定的文件,例如: ```lua target("test") add_files("src/*.c") del_files("src/test.c") ``` 灵活合理运用[add\_files](https://xmake.io/zh/)和[del\_files](https://xmake.io/zh/),我们可以极大程度的简化工程源码的配置管理。 ## 利用内置配置简化flags设置 对于一些常用的flags设置,例如:`-O3`, `-g`, `-std=c++11`等编译选项,xmake提供了一些更加通用内置的配置,来简化设置,用户不需要再去考虑不同平台、不同编译器上对应的这些flags的一些差异性,只需要设置: ```lua set_optimize("fastest") set_symbols("debug") set_languages("cxx11") set_warnings("all", "error") ``` 具体配置说明,以及目前提供的配置值,都可以去看下相关文档,里面有详细说明:[内置配置说明](https://xmake.io/zh/) --- --- url: /zh/posts/merge-static-library.md --- xmake的add\_files接口不仅可以添加源代码文件进行编译,还可以直接添加\*.o/obj对象文件、以及\*.a/lib的库文件到编译目标中,这个跟add\_links是有区别的 * add\_links:只能添加链接,例如: -lxxxx 这种,链接的目标也只能是可执行程序、动态库,而且只会链接需要的代码进去 * add\_files:是直接将静态库中的所有对象文件,解包、重新打包到新的target中,这个target可以是新的静态库,也可以是可执行程序、或者动态库 例如: ```lua target("test") -- 生成静态库:libtest.a set_kind("static") -- 添加对象文件 add_files("obj/*.o") -- 添加静态库,将里面的对象文件重新打包到libtest.a中,生成新的静态库 add_files("lib/*.a") ``` 这个target模块,可以没有任何源码,单纯的将所有静态库、对象文件重新打包到一个新的静态库中,当然再加一些源文件也是可以的 target的类型也没有限定,你也可以指定输出为动态库:shared,可执行程序:binary 例如: ```lua target("test2") -- 生成动态库:libtest2.so set_kind("shared") -- 添加对象文件 add_files("obj/*.o") -- 添加静态库libtest.a中的所有对象文件 add_files("lib/libtest.a") -- 添加一些源文件 add_files("src/*.c") ``` --- --- url: /zh/posts/batch-check-library-interfaces.md --- 有时候可能用到某个库的某些函数接口,但是这个库有可能在某个平台上被裁减过了,接口支持不全,如果你想跨平台使用,就会出问题 因此在使用之前进行检测是否存在这个函数,还是很有必要的,xmake提供了方便的api,可以批量检测某个库的一些函数: 例如: ```lua target("test") -- 检测libc库中,对宽字符操作的接口是否存在,检测条件:检查wchar.h、stdlib.h中是否有函数声明 add_cfuncs("libc", nil, {"wchar.h", "stdlib.h"}, "wcscat", "wcsncat", "wcscpy", "wcsncpy", "wcslcpy", "wcslen", "wcsnlen", "wcsstr", "wcscasestr", "wcscmp", "wcscasecmp", "wcsncmp", "wcsncasecmp", "wcstombs", "mbstowcs") -- 检测pthread库中,是否存在pthread_mutex_init, pthread_create接口,相当于检测了pthread是否存在 -- 第一个参数是库类型、别名,可以随便写 add_cfuncs("posix", nil, "pthread.h", "pthread_mutex_init", "pthread_create") -- 检测pthread库中,是否存在pthread_mutex_init, pthread_create接口,相当于检测了pthread是否存在 -- 这个检测更加严格,同时检测了libpthread.a静态库是否存在这个接口的定义,如果链接不通过,就检测失败 -- xmake会在检测时,尝试链接-lpthread add_cfuncs("posix", "pthread", "pthread.h", "pthread_mutex_init", "pthread_create") ``` 可以执行:`xmake f -v` 看到实际的检测信息,这里随便摘取了一段tbox中检测信息: ``` checking for the c include string.h ... ok checking for the c include stdlib.h ... ok checking for the c function strlen ... ok checking for the c function sincosf ... no checking for the c include wchar.h ... ok checking for the c function wcscmp ... ok checking for the c function wcsncat ... ok checking for the c include dlfcn.h ... ok checking for the c function dlopen ... ok checking for the links polarssl ... ok checking for the c include polarssl/polarssl.h ... ok checking for the c function strcat ... ok checking for the c function wcsstr ... ok checking for the c function wcscat ... ok checking for the c function sincos ... no checking for the c function memcpy ... ok checking for the c function sqrtf ... ok checking for the c function wcsnlen ... ok checking for the c function acosf ... ok checking for the links pthread, dl, m, c ... ok checking for the c include sys/stat.h ... ok checking for the c function open ... ok checking for the c function strnlen ... ok checking for the c function system ... ok checking for the links z ... ok checking for the c include zlib/zlib.h ... ok checking for the c function strncat ... ok checking for the c function wcsncpy ... ok checking for the c function gmtime ... ok checking for the c include signal.h ... ok checking for the c include setjmp.h ... ok checking for the c function sigsetjmp ... ok checking for the c function sinf ... ok checking for the c function strncmp ... ok checking for the c function memmove ... ok checking for the c function strncasecmp ... ok checking for the c function strlcpy ... ok checking for the links sqlite3 ... ok checking for the c include sqlite3/sqlite3.h ... ok checking for the c include sys/sem.h ... ok checking for the c include sys/ipc.h ... ok checking for the c function semtimedop ... no checking for the c function wcscpy ... ok checking for the c function sqrt ... ok checking for the c function strcmp ... ok checking for the c function strcasecmp ... ok checking for the c function semget ... ok checking for the c include unistd.h ... ok checking for the c function sysconf ... ok checking for the c function memset ... ok checking for the c function getpagesize ... ok checking for the c include semaphore.h ... ok checking for the c function sem_init ... ok checking for the c function strncpy ... ok checking for the c function localtime ... ok checking for the c include ifaddrs.h ... ok checking for the c function getifaddrs ... ok checking for the c function strcpy ... ok checking for the c function gethostname ... ok checking for the c function wcslcpy ... ok checking for the c include dirent.h ... ok checking for the c function opendir ... ok checking for the c function wcslen ... ok checking for the c function cos ... ok checking for the c include sys/time.h ... ok checking for the c function gettimeofday ... ok checking for the c function signal ... ok checking for the c function strstr ... ok checking for the c function exp ... ok checking for the c function log2f ... ok checking for the c function sin ... ok checking for the c function log2 ... ok checking for the c function cosf ... ok checking for the c include pthread.h ... ok checking for the c function pthread_mutex_init ... ok checking for the c function fmodf ... ok checking for the c function wcstombs ... ok checking for the c function fmod ... ok checking for the c function memcmp ... ok checking for the c function atan2f ... ok checking for the c function atan2 ... ok checking for the c function atanf ... ok checking for the c function atan ... ok checking for the c function powf ... ok checking for the c function pow ... ok checking for the c function asinf ... ok checking for the c function asin ... ok checking for the c function pthread_create ... ok ``` 最后的检测结果会自动输出到config.h中(如果有启用的话): ```c #define TB_CONFIG_LIBC_HAVE_MEMCPY #define TB_CONFIG_LIBC_HAVE_MEMSET #define TB_CONFIG_LIBC_HAVE_MEMMOVE #define TB_CONFIG_LIBC_HAVE_MEMCMP #define TB_CONFIG_LIBC_HAVE_MEMMEM #define TB_CONFIG_LIBC_HAVE_STRCAT #define TB_CONFIG_LIBC_HAVE_STRNCAT #define TB_CONFIG_LIBC_HAVE_STRCPY #define TB_CONFIG_LIBC_HAVE_STRNCPY #define TB_CONFIG_LIBC_HAVE_STRLCPY #define TB_CONFIG_LIBC_HAVE_STRLEN #define TB_CONFIG_LIBC_HAVE_STRNLEN #define TB_CONFIG_LIBC_HAVE_STRSTR #define TB_CONFIG_LIBC_HAVE_STRCASESTR #define TB_CONFIG_LIBC_HAVE_STRCMP #define TB_CONFIG_LIBC_HAVE_STRCASECMP #define TB_CONFIG_LIBC_HAVE_STRNCMP #define TB_CONFIG_LIBC_HAVE_STRNCASECMP #define TB_CONFIG_LIBC_HAVE_WCSCAT #define TB_CONFIG_LIBC_HAVE_WCSNCAT #define TB_CONFIG_LIBC_HAVE_WCSCPY #define TB_CONFIG_LIBC_HAVE_WCSNCPY #define TB_CONFIG_LIBC_HAVE_WCSLCPY #define TB_CONFIG_LIBC_HAVE_WCSLEN #define TB_CONFIG_LIBC_HAVE_WCSNLEN #define TB_CONFIG_LIBC_HAVE_WCSSTR #define TB_CONFIG_LIBC_HAVE_WCSCMP #define TB_CONFIG_LIBC_HAVE_WCSCASECMP #define TB_CONFIG_LIBC_HAVE_WCSNCMP #define TB_CONFIG_LIBC_HAVE_WCSNCASECMP #define TB_CONFIG_LIBC_HAVE_WCSTOMBS #define TB_CONFIG_LIBC_HAVE_MBSTOWCS #define TB_CONFIG_LIBC_HAVE_GMTIME #define TB_CONFIG_LIBC_HAVE_MKTIME #define TB_CONFIG_LIBC_HAVE_LOCALTIME #define TB_CONFIG_LIBC_HAVE_GETTIMEOFDAY #define TB_CONFIG_LIBC_HAVE_SIGNAL #define TB_CONFIG_LIBC_HAVE_SETJMP #define TB_CONFIG_LIBC_HAVE_SIGSETJMP #define TB_CONFIG_LIBC_HAVE_BACKTRACE #define TB_CONFIG_LIBC_HAVE_SYSTEM #define TB_CONFIG_LIBM_HAVE_LOG2 #define TB_CONFIG_LIBM_HAVE_LOG2F #define TB_CONFIG_LIBM_HAVE_SQRT #define TB_CONFIG_LIBM_HAVE_SQRTF #define TB_CONFIG_LIBM_HAVE_ACOS #define TB_CONFIG_LIBM_HAVE_ACOSF #define TB_CONFIG_LIBM_HAVE_ASIN #define TB_CONFIG_LIBM_HAVE_ASINF #define TB_CONFIG_LIBM_HAVE_POW #define TB_CONFIG_LIBM_HAVE_POWF #define TB_CONFIG_LIBM_HAVE_FMOD #define TB_CONFIG_LIBM_HAVE_FMODF #define TB_CONFIG_LIBM_HAVE_ATAN #define TB_CONFIG_LIBM_HAVE_ATANF #define TB_CONFIG_LIBM_HAVE_ATAN2 #define TB_CONFIG_LIBM_HAVE_ATAN2F #define TB_CONFIG_LIBM_HAVE_COS #define TB_CONFIG_LIBM_HAVE_COSF #define TB_CONFIG_LIBM_HAVE_SIN #define TB_CONFIG_LIBM_HAVE_SINF #define TB_CONFIG_LIBM_HAVE_EXP #define TB_CONFIG_LIBM_HAVE_EXPF #define TB_CONFIG_POSIX_HAVE_POLL #define TB_CONFIG_POSIX_HAVE_PTHREAD_MUTEX_INIT #define TB_CONFIG_POSIX_HAVE_PTHREAD_CREATE #define TB_CONFIG_POSIX_HAVE_SOCKET #define TB_CONFIG_POSIX_HAVE_OPENDIR #define TB_CONFIG_POSIX_HAVE_DLOPEN #define TB_CONFIG_POSIX_HAVE_OPEN #define TB_CONFIG_POSIX_HAVE_GETHOSTNAME #define TB_CONFIG_POSIX_HAVE_GETIFADDRS #define TB_CONFIG_POSIX_HAVE_SEM_INIT #define TB_CONFIG_POSIX_HAVE_GETPAGESIZE #define TB_CONFIG_POSIX_HAVE_SYSCONF #define TB_CONFIG_POSIX_HAVE_SCHED_YIELD #define TB_CONFIG_SYSTEMV_HAVE_SEMGET ``` 之后就可以在代码中,包含这个config.h来判断是否需要实际调用这个接口了,如果要多c++代码的接口进行检测,只需把名字改成: ```lua add_cxxfuncs(...) ``` 就行了,更加详细的检测设置,可以参考[依赖包的添加和自动检测机制](https://xmake.io/zh/) --- --- url: /zh/posts/custom-option.md --- xmake还可以支持一些自定义选项开关,使得工程支持可选编译,方便工程的模块化管理。 ## 增加自定义编译开关 我们拿一个实际的例子来说: 我们想在自己的工程中增加一个新开关选项:hello, 如果这个开关被启用,会在target中添加特定的一些源码文件,但是这个开挂默认是不被启用的,需要通过配置`xmake f --hello=true`才会被链接和使用 并且使用的时候,需要定义一些特殊的宏定义:`-DHELLO_TEST -DHELLO_ENABLE` 那么我们开始进行xmake.lua修改,过程并不复杂: 1. 在xmake.lua的头部通过option接口定义一个名叫hello的开关选项 ```lua --定义一个名叫hello的开关选项,这个接口跟add_target是同级的,不要在add_target里面使用(使用了也没什么问题,只是不大好看) option("hello") -- 默认禁用这个开关,需要手动xmake f --hello=true才会启用,当然你也可以默认启用它 set_default(false) -- 定义一些宏开关,这个只有在hello被启用的时候才会被定义 add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") ``` 2. 将定义好的hello开关选项,与你的target项目进行绑定 ```lua -- 添加一个test目标 target("test") -- 生成可执行程序 set_kind("binary") -- 绑定hello开关选项 add_options("hello") -- 添加一些hello才需要的源码文件 if options("hello") then add_files("hello/*.c") end ``` ok了,只要两步,接下来就是编译了: ```bash # 直接编译,默认是禁用hello的,所以hello的相关代码,都没有被编译进去 $ xmake # 接下来我们启用它,重新编译下,这个时候,hello/*.c的代码也被编译进去了,同时-DHELLO_TEST -DHELLO_ENABLE也被添加到编译选项中了 $ xmake f --hello=true $ xmake -r ``` 很方便吧。。只需两步就行。。接下来,我们再稍微修饰下: ```lua option("hello") -- 默认禁用这个开关,需要手动xmake f --hello=true才会启用,当然你也可以默认启用它 set_default(false) -- 定义一些宏开关,这个只有在hello被启用的时候才会被定义 add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") -- 启用显示菜单,这样xmake f --help的时候,你这个新加的开关就会被显示出来 set_showmenu(true) -- 对菜单中开关进行分类,这样显示的时候 布局会更好看,这个不是必须的 set_category("module_xxx") -- 在菜单中,对这个开关进行详细描述 set_description("Enable or disable the hello module") ``` 这个时候,你再敲下: ```bash $ xmake f --help ``` 会显示如下菜单信息: ``` 此处省略... --hello=HELLO Enable or disable the hello module (default: false) 此处省略... ``` 这样给别人看的时候,也就更明了些。。。 ## 自动检测机制 接下来,我们整的稍微复杂些,让这个hello被启用的时候,自动链接上libhello.a库,并且可以对libhello.a进行自动检测,如果不存在,就禁用hello开关。。 修改如下: ```lua option("hello") -- 默认禁用这个开关,需要手动xmake f --hello=true才会启用,当然你也可以默认启用它 set_default(false) -- 定义一些宏开关,这个只有在hello被启用的时候才会被定义 add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") -- 启用显示菜单,这样xmake f --help的时候,你这个新加的开关就会被显示出来 set_showmenu(true) -- 在菜单中,对这个开关进行详细描述 set_description("Enable or disable the hello module") -- 添加链接库libhello.a,这个在xmake f 会去自动检测,如果检测链接不通过,那么这个开关就会被禁用掉 -- 如果ok,编译的时候会自动加上-lhello add_links("hello") -- 添加链接库检测搜索目录,如果路径不对,检测就会链接不通过,如果ok,在编译的时候,会自动加上-L./libs add_linkdirs("libs") ``` 修改后,如果这个hello开关被手动启用,或者自动检测通过,会在编译连接的时候,自动加上-L./libs -lhello的连接选项。 ## 增加一些其他的检测规则 针对自动检测,除了可以检测链接库,还可以增加一些其他的检测规则: * 检测头文件是否能够正常包含 * 类型定义是否存在 * 接口api是否存在 * 检测链接库是否能够正常链接 例如: ```lua option("hello") -- 默认禁用这个开关,需要手动xmake f --hello=true才会启用,当然你也可以默认启用它 set_default(false) -- 定义一些宏开关,这个只有在hello被启用的时候才会被定义 add_defines_if_ok("HELLO_ENABLE", "HELLO_TEST") -- 启用显示菜单,这样xmake f --help的时候,你这个新加的开关就会被显示出来 set_showmenu(true) -- 在菜单中,对这个开关进行详细描述 set_description("Enable or disable the hello module") -- 添加链接库libhello.a,这个在xmake f 会去自动检测,如果检测链接不通过,那么这个开关就会被禁用掉 -- 如果ok,编译的时候会自动加上-lhello add_links("hello") -- 添加链接库检测搜索目录,如果路径不对,检测就会链接不通过,如果ok,在编译的时候,会自动加上-L./libs add_linkdirs("libs") -- 检测在c代码中: include "hello/hello.h",是否成功,ok的话才启用hello -- 检测c++代码请使用:add_cxxincludes add_cincludes("hello/hello.h") -- 添加头文件检测路径,ok的话,会自动加上:-Iinc/xxx -I./inc的 编译选项 add_includedirs("inc/$(plat)", "inc") -- 检测对c代码类型wchar_t的支持,如果不存在这个类型,就检测失败 -- 检测会依赖add_cincludes中提供的头文件,如果给定的头文件中定义了这个类型,就能检测通过 -- 检测c++代码请使用:add_cxxtypes add_ctypes("wchar_t") -- 检测对c代码中是否存在接口api:hello_test() -- 检测会依赖add_cincludes中提供的头文件,如果给定的头文件中定义了这个类型,就能检测通过 -- 检测c++代码请使用:add_cxxfuncs add_cfuncs("hello_test") ``` 需要注意的是,所有的检测都是and关系,必须全部通过,才会自动启用hello开关。 ## 其他可以被自动添加的api 并且在检测ok或者被手动启用后,可以自动添加一些特殊的编译选项、宏定义,这些接口如下: * `add_cflags`:选项开关被启用后,自动添加c编译选项 * `add_cxflags`:选项开关被启用后,自动添加c/c++编译选项 * `add_cxxflags`:选项开关被启用后,自动添加c++编译选项 * `add_ldflags`:选项开关被启用后,自动添加链接选项 * `add_vectorexts`:选项开关被启用后,自动添加指令扩展选项,例如:mmx, sse ... ## 自动生成config.h配置文件 option不仅可以在编译的时候,自动添加编译选项,还可以在启用后,自动生成各种宏开关到config.h文件中,方便我们在代码里面控制编译逻辑 具体的使用说明,见:[依赖包的添加和自动检测机制](https://xmake.io/zh/) --- --- url: /zh/posts/binding-option.md --- 什么是选项的绑定呢? 例如我想在命令行中配置一个smallest的参数:`xmake f --smallest=y` 这个时候,需要同时禁用多个其他的选项开关,来禁止编译多个模块,就是这个需求,相当于一个选项 与其他 多个选项之间 是有联动效应的。。 那如何实现呢,可以通过下面两个api来实现: * add\_bindings: 添加正向绑定 * add\_rbindings: 添加反向绑定 我们看下如何实现smallest的这个效果: ```lua -- 定义选项开关: --smallest=y|n option("smallest") -- 默认不启用 set_enable(false) -- 在命令行菜单中显示描述,并且可手动配置 set_showmenu(true) -- 设置描述 set_description("Enable the smallest compile mode and disable all modules.") -- 添加反向绑定,如果smallest被启用,下面的所有模块全部禁用 add_rbindings("xml", "zip", "asio", "regex", "object", "thread", "network", "charset", "database") add_rbindings("zlib", "mysql", "sqlite3", "openssl", "polarssl", "pcre2", "pcre", "base") ``` 需要注意的是,命令行配置是有顺序的,你可以先通过启用smallest禁用所有模块,然后添加其他选项,逐一启用,例如: ```lua -- 禁用所有模块,然后仅仅启用xml和zip模块 xmake f --smallest=y --xml=y --zip=y ``` --- --- url: /zh/posts/enable-pdb-on-windows.md --- 之前xmake默认编译windows目标,debug模式下采用的是`-Z7`编译选项,内置的调试符号信息到obj文件里面 但是这种方式按msdn的文档上说,是属于旧式的调试符号文件格式,所以为了考虑后续的兼容性,xmake修改了默认的调试符号生成规则, 改为默认启用pdb符号文件,并且pdb的方式更为常用。。 这个行为的修改,并不会影响到`xmake.lua`的设置,如果在这个文件中,设置了启用调试符号: ```lua set_symbols("debug") ``` 那么,编译debug版本的目标时,就会自动生成pdb文件,以tbox为例: ```bash $ xmake f -m debug $ xmake ``` 编译完成后,会自动在build目录下生成两个pdb文件: ``` build\tbox.pdb build\demo.pdb ``` 一个是静态库的pdb文件,一个是demo程序的pdb文件,并且如果我们执行打包命令: ```bash $ xmake package ``` 的话,也会在包目录里面,将pdb文件也给自动打包进去。。。 --- --- url: /api/description/xpack-component-interfaces.md --- # XPack Component Interfaces ## set\_title * Set a brief description of the package components #### Function Prototype ::: tip API ```lua set_title(title: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | title | Component title string | #### Usage ```lua xpack_component("LongPath") set_title("Enable Long Path") ``` ## set\_description * Set detailed description of package components #### Function Prototype ::: tip API ```lua set_description(description: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | description | Component description string | #### Usage ```lua xpack_component("LongPath") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") ``` ## set\_default * Set the default enabled state of package components #### Function Prototype ::: tip API ```lua set_default(default: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | default | Whether component is enabled by default (boolean) | #### Usage Usually the package component is enabled by default, but we can also use this interface to disable this component by default. Only when the user chooses to check this component when installing the package will it be enabled for installation. ```lua xpack_component("LongPath") set_default(false) set_title("Enable Long Path") ``` ## on\_load * Custom loading script #### Function Prototype ::: tip API ```lua on_load(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Load script function with component parameter | #### Usage We can further flexibly configure package components in the on\_load custom script field. ```lua xpack_component("test") on_load(function (component) local package = component:package() -- TODO end) ``` ## before\_installcmd * Add script before component installation #### Function Prototype ::: tip API ```lua before_installcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before install script function with component and batchcmds parameters | #### Usage It will not rewrite the entire installation script, but will add some custom installation scripts before the existing installation scripts are executed: ```lua xpack_component("test") before_installcmd(function (component, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) ``` It should be noted that the cp, mkdir and other commands added through `batchcmds` will not be executed immediately, but will only generate a command list. When the package is actually generated later, these commands will be translated into packaging commands. It is exactly the same as xpack's before\_installcmd. The only difference is that the installation script here will only be executed when this component is enabled. ## on\_installcmd * Rewrite the installation script of the component #### Function Prototype ::: tip API ```lua on_installcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Install script function with component and batchcmds parameters | #### Usage Custom installation script for implementing specific component installation logic. This will rewrite the entire component's installation script, similar to xpack's on\_installcmd. ```lua xpack_component("test") on_installcmd(function (component, batchcmds) -- TODO end) ``` ## after\_installcmd * Add script after component installation #### Function Prototype ::: tip API ```lua after_installcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After install script function with component and batchcmds parameters | #### Usage Custom script executed after component installation, used for post-processing operations. After the component is installed, the script here will be executed, similar to xpack's after\_installcmd. ```lua xpack_component("test") after_installcmd(function (component, batchcmds) -- TODO end) ``` ## before\_uninstallcmd * Add script before component uninstallation #### Function Prototype ::: tip API ```lua before_uninstallcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before uninstall script function with component and batchcmds parameters | After the component is installed, the script here will be executed, similar to xpack's before\_uninstallcmd. ```lua xpack_component("test") before_uninstallcmd(function (component, batchcmds) -- TODO end) ``` ## on\_uninstallcmd * Rewrite the script for component uninstallation #### Function Prototype ::: tip API ```lua on_uninstallcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Uninstall script function with component and batchcmds parameters | #### Usage Custom uninstall script for implementing specific component uninstall logic. This will rewrite the entire component's uninstall script, similar to xpack's on\_uninstallcmd. ```lua xpack_component("test") on_uninstallcmd(function (component, batchcmds) -- TODO end) ``` ## after\_uninstallcmd * Add script after component uninstallation #### Function Prototype ::: tip API ```lua after_uninstallcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After uninstall script function with component and batchcmds parameters | #### Usage Custom script executed after component uninstallation, used for post-processing operations. After the component is uninstalled, the script here will be executed, similar to xpack's before\_uninstallcmd. ```lua xpack_component("test") before_uninstallcmd(function (component, batchcmds) -- TODO end) ``` ## add\_sourcefiles * Add component source file #### Function Prototype ::: tip API ```lua add_sourcefiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | files | Source file pattern string or array | | ... | Variable parameters, can pass multiple file patterns | | prefixdir | Installation prefix directory | | rootdir | Source root directory | | filename | Target filename | This is similar to xpack's add\_sourcefiles, but here only when the component is enabled, these source files will be added to the installation package. ## add\_installfiles * Add component binary installation file #### Function Prototype ::: tip API ```lua add_installfiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | files | Install file pattern string or array | | ... | Variable parameters, can pass multiple file patterns | | prefixdir | Installation prefix directory | | rootdir | Source root directory | | filename | Target filename | #### Usage This is similar to xpack's add\_installfiles, but here only the binaries are added to the installation package when the component is enabled. --- --- url: /api/description/xpack-interfaces.md --- # XPack Interfaces Xpack is provided as a plug-in, and all its APIs need to be introduced through `includes("@builtin/xpack")`. ```lua includes("@builtin/xpack") xpack("test") set_version("1.0") set_homepage("https://xmake.io") add_installfiles("...") ``` ## set\_version * Set package version #### Function Prototype ::: tip API ```lua set_version(version: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | version | Package version string | #### Usage This interface is used to set the version of the generated installation package: ```lua xpack("test") set_version("1.0") --... ``` If we do not set it, but bind the installed target program through `add_targets`, the version configuration in target will also be used. ```lua target("foo") set_version("1.0") xpack("test") add_targets("foo") --... ``` We can also use the global project version if no targets are bound. ```lua set_version("1.0") xpack("xmake") --... ``` ## set\_homepage * Set homepage information #### Function Prototype ::: tip API ```lua set_homepage(homepage: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | homepage | Homepage URL string | #### Usage ```lua xpack("xmake") set_homepage("https://xmake.io") ``` ## set\_title * Set title information #### Function Prototype ::: tip API ```lua set_title(title: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | title | Package title string | #### Usage A simple description usually used to configure the installation package, shorter than `set_description`. ```lua xpack("xmake") set_title("Xmake build utility ($(arch))") ``` ## set\_description * Set detailed description #### Function Prototype ::: tip API ```lua set_description(description: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | description | Package description string | #### Usage This interface can set more detailed description information of the installation package. You can use one or two sentences to describe the package in detail. ```lua xpack("xmake") set_description("A cross-platform build utility based on Lua.") ``` ## set\_author * Set author information #### Function Prototype ::: tip API ```lua set_author(author: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | author | Author information string | #### Usage We can set the email address, name, etc. to describe the author of this package. ```lua xpack("xmake") set_author("waruqi@gmail.com") ``` ## set\_maintainer * Set maintainer information #### Function Prototype ::: tip API ```lua set_maintainer(maintainer: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | maintainer | Maintainer information string | #### Usage We can set the email address, name, etc. to describe the maintainer of this package. The maintainer and author may or may not be the same person. ```lua xpack("xmake") set_maintainer("waruqi@gmail.com") ``` ## set\_copyright * Set the copyright information of the package #### Function Prototype ::: tip API ```lua set_copyright(copyright: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | copyright | Copyright information string | ```lua xpack("xmake") set_copyright("Copyright (C) 2015-present, TBOOX Open Source Group") ``` ## set\_license * Set the package licence #### Function Prototype ::: tip API ```lua set_license(license: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | license | License name string | Currently used by packages like srpm/rpm/deb to set the licence name. ```lua set_license("Apache-2.0") ``` ## set\_licensefile * Set the license file of the package #### Function Prototype ::: tip API ```lua set_licensefile(licensefile: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | licensefile | License file path string | We can set the file path where LICENSE is located, like the NSIS installation package, it will also additionally display the LICENSE page to the installation user. ```lua xpack("xmake") set_licensefile("../LICENSE.md") ``` ## set\_company * Set the company to which the package belongs #### Function Prototype ::: tip API ```lua set_company(company: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | company | Company name string | We can use this interface to set the company and organization name to which the package belongs. ```lua xpack("xmake") set_company("tboox.org") ``` ## set\_inputkind * Set the packaged input source type #### Function Prototype ::: tip API ```lua set_inputkind(inputkind: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | inputkind | Input source type: "binary" or "source" | This is an optional interface that can be used to identify the currently packaged input source type. * binary: package from binary files as input source, usually using `add_installfiles` * source: Start packaging from a source file as an input source, usually using `add_sourcefiles` This is generally used for custom packaging formats, and for built-in formats, such as: nsis, zip, srczip, etc., In fact, it can be determined whether the currently packaged input source is packaged from the source code or directly from the binary source. Therefore, unless necessary (such as customizing the packaging format), we usually do not need to set it. In the script domain, we can also use `package:from_source()` and `package:from_binary()` to determine the current input source. ```lua xpack("test") set_formats("nsis", "zip", "targz", "srczip", "srctargz", "runself") add_installfiles("src/(assets/*.png)", {prefixdir = "images"}) add_sourcefiles("(src/**)") on_load(function (package) if package:from_source() then package:set("basename", "test-$(plat)-src-v$(version)") else package:set("basename", "test-$(plat)-$(arch)-v$(version)") end end) ``` If the above packaging configuration is an nsis package, the binary file will be used as the input source for packaging by default, and the files configured by `add_installfiles` will be packaged. `srczip`, `srctargz` and `runself` start packaging from the source file, will package the files in `add_sourcefiles`, and then execute the packaging script. ## set\_formats * Set packaging format #### Function Prototype ::: tip API ```lua set_formats(formats: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | formats | Package format name string or array | | ... | Variable parameters, can pass multiple format names | Configure the packaging format that needs to be generated by the current XPack package. Multiple formats can be configured at the same time. The `xmake pack` command will generate them all at once. ::: tip NOTE Some formats will be automatically ignored if the current platform does not support generation. ::: ```lua xpack("test") set_formats("nsis", "zip", "targz", "srczip", "srctargz", "runself") ``` We can also specify to generate some of the formats through commands instead of generating them all at once. ```sh $ xmake pack -f "nsis,zip" ``` Separated by commas, specify to generate NSIS and zip packages, and ignore other format packages for the time being. Currently supported formats are: | Format | Description | | ---- | ---- | | nsis | Windows NSIS installation package, binary installation | | zip | Binary zip package, does not contain installation script | | targz | Binary tar.gz package, does not contain the installation script | | srczip | zip source package | | srctargz | tar.gz source package | | runself | self-running shell script package, source code compilation and installation | | rpm | rpm binary installation package | | srpm | rpm source code installation package | | deb | deb binary installation package (TODO) | | Others | Customizable formats and installation scripts | ## set\_basename * Set package file name #### Function Prototype ::: tip API ```lua set_basename(basename: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | basename | Package base name string | Set the file name of the generated package, but do not include the suffix. ```lua xpack("xmake") set_basename("xmake-v$(version)") ``` We can also configure variables such as `$(version)`, `$(plat)`, `$(arch)` and so on. In addition, if you want more flexible configuration, you can configure it in the on\_load script. ```lua xpack("xmake") on_load(function (package) package:set("basename", "xmake-v" .. package:version()) end) ``` ## set\_extension * Set the extension of the installation package #### Function Prototype ::: tip API ```lua set_extension(extension: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | extension | Package file extension string | Usually we do not need to modify the extension of the generated package, because after specifying `nsis`, `zip` and other formats, there will be a default suffix name, such as: `.exe`, `.zip`. However, if we are customizing the package format and need to generate a custom package, then we may need to configure it. ```lua xpack("mypack") set_format("myformat") set_extension(".myf") on_package(function (package) local outputfile = package:outputfile() -- TODO end) ``` For example, here we customize a myformat package format, using the custom suffix name of `.myf`, and then we can generate it in on\_package, The package output file name returned by `package:outputfile()` will contain this suffix. ## add\_targets * Associated target program #### Function Prototype ::: tip API ```lua add_targets(targets: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | targets | Target name string or array | | ... | Variable parameters, can pass multiple target names | We can use this interface to configure the associated target that needs to be installed. ```lua target("foo") set_kind("shared") add_files("src/*.cpp") add_headerfiles("include/(*.h)") xpack("test") set_formats("nsis") add_targets("foo") ``` When the test installation package is generated, the executable program and dynamic library of the associated foo target will be packaged and installed together. In addition, the custom installation files configured through `add_headerfiles` and `add_installfiles` in the target will also be included in the installation package and installed together. And we can also use `on_installcmd`, `after_installcmd` and other custom packaging installation scripts in the target and its rules, which will also be executed together. ## add\_components * Add installation package components #### Function Prototype ::: tip API ```lua add_components(components: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | components | Component name string or array | | ... | Variable parameters, can pass multiple component names | We also support adding custom components to the installation package and selecting and installing them according to the component mode. Currently, only NSIS packages have comparative support effects. We can define a component domain through `xpack_component()`, and then use `add_components()` to add the specified component and associate it with the package. In the component, we can write some custom installation scripts through `on_installcmd()`, and the installation will only be executed when the component is enabled. ```lua xpack("test") add_components("LongPath") xpack_component("LongPath") set_default(false) set_title("Enable Long Path") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") on_installcmd(function (component, batchcmds) batchcmds:rawcmd("nsis", [[ ${If} $NoAdmin == "false" ; Enable long path WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ${EndIf}]]) end) ``` Here, we use `batchcmds:rawcmd("nsis", "...")` to add an nsis-specific installation command to enable long path support. The effect is as follows: ![](/assets/img/manual/nsis_4.png) It will only be enabled when we check LongPath. Of course, we can also configure whether the component is enabled by default through `set_default()`. Except for the NSIS package, although other packages do not have complete support for components, they will also execute the scripts in the components to implement packaging, but may not be able to display the corresponding component UI and check boxes. ## set\_bindir * Set the binary installation directory of the package #### Function Prototype ::: tip API ```lua set_bindir(bindir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | bindir | Binary directory path string | Usually the generated installation package will have an installation root directory, and we can specify the bin directory location under the installation directory through this configuration. If not specified, defaults to `installdir/bin`. If configured ```lua xpack("xmake") set_bindir("mybin") ``` Then the executable file will be installed under `installdir/mybin`. If it is an NSIS package, this path will be automatically set to `%PATH%` after installation. ## set\_libdir * Set the library installation directory of the package #### Function Prototype ::: tip API ```lua set_libdir(libdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | libdir | Library directory path string | Usually the generated installation package will have an installation root directory, and we can specify the lib directory location under the installation directory through this configuration. If not specified, defaults to `installdir/lib`. If configured ```lua xpack("xmake") set_libdir("mylib") ``` Then the static library files will be installed under `installdir/mylib`. ## set\_includedir * Set the package header file installation directory #### Function Prototype ::: tip API ```lua set_includedir(includedir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | includedir | Include directory path string | Usually the generated installation package will have an installation root directory, and we can specify the include directory location under the installation directory through this configuration. If not specified, defaults to `installdir/include`. If configured ```lua xpack("xmake") set_includedir("myinc") ``` Then the header files will be installed under `installdir/myinc`. ## set\_prefixdir * Set the installation prefix directory of the package #### Function Prototype ::: tip API ```lua set_prefixdir(prefixdir: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | prefixdir | Prefix directory path string | If configured ```lua xpack("xmake") set_prefixdir("prefix") ``` Then all installation files will be installed under `installdir/prefix`, for example: ``` installdir - prefix - include - lib - bin ``` ## set\_specfile * Set package spec file path #### Function Prototype ::: tip API ```lua set_specfile(specfile: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | specfile | Spec file path string | The generation of some package formats requires the generation of specific spec files before calling third-party packaging tools to generate packages. For example, for NSIS packages, you need to first generate an NSIS-specific `.nsi` configuration file through xmake based on the xpack configuration, and then xmake will call `makensis.exe` to generate the NSIS package based on this `.nsi` file. Packages such as deb/rpm have specific spec files. xmake will automatically generate a spec file by default when packaging, but if we want to more deeply customize the configuration of some unique packages, we can use this interface, Configure a spec file of your own, in which the user maintains some package configuration definitions, and then define some `${PACKAGE_NAME}`, `${VERSION}` package-specific built-in variables in it to realize package information replacement. ```lua xpack("xmake") set_formats("nsis") set_specfile("makensis.nsi") ``` makensis.nsi ``` VIProductVersion "${VERSION}.0" VIFileVersion "${VERSION}.0" VIAddVersionKey /LANG=0 ProductName "${PACKAGE_NAME}" VIAddVersionKey /LANG=0 Comments "${PACKAGE_DESCRIPTION}" VIAddVersionKey /LANG=0 CompanyName "${PACKAGE_COMPANY}" VIAddVersionKey /LANG=0 LegalCopyright "${PACKAGE_COPYRIGHT}" VIAddVersionKey /LANG=0 FileDescription "${PACKAGE_NAME} Installer - v${VERSION}" VIAddVersionKey /LANG=0 OriginalFilename "${PACKAGE_FILENAME}" ``` Here are some built-in commonly used package variables: | Variable name | Description | | ------ | ---- | | PACKAGE\_ARCH | Architecture of package binaries | | PACKAGE\_PLAT | Platform for package binaries | | PACKAGE\_NAME | Package name | | PACKAGE\_TITLE | Brief description of the package | | PACKAGE\_DESCRIPTION | Detailed description of the package | | PACKAGE\_FILENAME | Package file name | | PACKAGE\_AUTHOR | package author | | PACKAGE\_MAINTAINER | Package maintainer | | PACKAGE\_HOMEPAGE | Package homepage address | | PACKAGE\_COPYRIGHT | Package copyright information | | PACKAGE\_COMPANY | The name of the company to which the package belongs | | PACKAGE\_ICONFILE | Package icon file path | | PACKAGE\_LICENSEFILE | Package LICENSE file path | | PACKAGE\_VERSION\_MAJOR | The major version of the package | | PACKAGE\_VERSION\_MINOR | The minor version of the package | | PACKAGE\_VERSION\_ALTER | Alter version of package | | PACKAGE\_VERSION\_BUILD | The build version of the package | In addition to built-in variables, we can also configure some custom template variables through the `set_specvar` interface. ## set\_specvar * Set custom variables in the package spec file #### Function Prototype ::: tip API ```lua set_specvar(name: , value: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | name | Variable name string | | value | Variable value string | Usually used together with the `set_specfile` interface to set some custom package variables in a custom spec template file. ```lua xpack("xmake") set_formats("nsis") set_specfile("makensis.nsi") set_specvar("FOO", "hello") ``` makensis.nsi ``` VIAddVersionKey /LANG=0 ProductName "${FOO}" ``` Before generating the package, xmake will replace `${FOO}` with hello, and then call the `makensis.exe` command to generate the NSIS installation package based on this file. ## set\_iconfile * Set icon file path #### Function Prototype ::: tip API ```lua set_iconfile(iconfile: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | iconfile | Icon file path string | We can additionally configure an ico icon file, which can be used to set the icon of some installation packages such as NSIS that support icon customization. ```lua xpack("xmake") set_iconfile("xmake.ico") ``` ## add\_sourcefiles * Add source files #### Function Prototype ::: tip API ```lua add_sourcefiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | files | Source file pattern string or array | | ... | Variable parameters, can pass multiple file patterns | | prefixdir | Installation prefix directory | | rootdir | Source root directory | | filename | Target filename | This is usually used for source packages, that is, pure source packages such as `srczip`, `srctargz`, and source code installation packages in the `runself` format. If it is a custom package format, we need to configure `set_inputkind("source")` to open the source package. Through this interface, you can customize which source files need to be included in the package for later compilation and installation. Its detailed usage is similar to `add_installfiles`, you can refer to its documentation description. ## add\_installfiles * Add binary files #### Function Prototype ::: tip API ```lua add_installfiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | files | Install file pattern string or array | | ... | Variable parameters, can pass multiple file patterns | | prefixdir | Installation prefix directory | | rootdir | Source root directory | | filename | Target filename | This is usually used for binary packages, that is, packages in `nsis`, `deb`, etc. formats, which install binary files directly. Therefore, we can use this interface to configure additional binary files that need to be installed, such as executable files, resource files, etc. For example, we can specify to install various types of files to the installation directory: ```lua xpack("test") add_installfiles("src/*.h") add_installfiles("doc/*.md") ``` We can also specify to install to a specific subdirectory: ```lua xpack("test") add_installfiles("src/*.h", {prefixdir = "include"}) add_installfiles("doc/*.md", {prefixdir = "share/doc"}) ``` For the above settings, we will install them to `installdir/include/*.h`, `installdir/share/doc/*.md`. Note: The default installation will not retain the directory structure and will be fully expanded. Of course, we can also use `()` to extract the subdirectory structure in the source file for installation, for example: ```lua xpack("test") add_installfiles("src/(tbox/*.h)", {prefixdir = "include"}) add_installfiles("doc/(tbox/*.md)", {prefixdir = "share/doc"}) ``` ## add\_buildrequires * Add package build dependencies #### Function Prototype ::: tip API ```lua add_buildrequires(requires: , ...) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | requires | Build requirement name string or array | | ... | Variable parameters, can pass multiple requirement names | This is usually used for some source packages, such as srpm. Before installing these source code packages, you need to build the source code first, and building the source code may require the use of some other dependency packages. We can configure them through this interface. ```lua xpack("test") set_formats("srpm") on_load(function (package) local format = package:format() if format == "srpm" then package:add("buildrequires", "make") package:add("buildrequires", "gcc") package:add("buildrequires", "gcc-c++") end end) on_buildcmd(function (package, batchcmds) batchcmds:runv("make") end) ``` Since different installation packages have some differences in their dependent package names, we need to configure them for different package formats in the on\_load script domain. ## on\_load * Custom loading script #### Function Prototype ::: tip API ```lua on_load(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Load script function with package parameter | If the configuration in the description field cannot meet our needs, we can further flexibly configure the package in the on\_load custom script field. This interface will be called during the initial loading of each XPack package, and you can make some basic configurations in it. For example, dynamically modify the package file name in it: ```lua xpack("test") on_load(function (package)package:set("basename", "test-" .. package:version()) end) ``` ## before\_package * Customize the script before packaging #### Function Prototype ::: tip API ```lua before_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before package script function with package parameter | We can configure custom scripts before packaging through this interface. ```lua xpack("test") before_package(function (package) -- TODO end) ``` ## on\_package * Custom packaging script #### Function Prototype ::: tip API ```lua on_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Package script function with package parameter | We can configure packaging custom scripts through this interface, which will rewrite the entire built-in packaging logic. Typically used for custom package formats. ```lua xpack("test") set_formats("xxx") on_package(function (package) -- TODO end) ``` ## after\_package * Customize the script after packaging #### Function Prototype ::: tip API ```lua after_package(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After package script function with package parameter | We can configure the custom script after packaging through this interface. ```lua xpack("test") after_package(function (package) -- TODO end) ``` ## on\_buildcmd * Custom build script #### Function Prototype ::: tip API ```lua on_buildcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Build script function with package and batchcmds parameters | For some source code build packages, we need to build the source code first before installation, such as srpm packages. Therefore, we can customize the build script through this interface, for example: ```lua xpack("test") set_formats("srpm") add_sourcefiles("src/*.c") add_sourcefiles("./configure") on_buildcmd(function (package, batchcmds) batchcmds:runv("./configure") batchcmds:runv("make") end) ``` If we associate target programs through add\_targets, xpack will execute the `xmake build` command by default to build them even if we do not configure `on_buildcmd`. ```lua xpack("test") set_formats("srpm") add_sourcefiles("src/*.c") add_sourcefiles("./xmake.lua") ``` In addition, we can also use `add_buildrequires` to configure some build dependencies. ## before\_buildcmd * Customize pre-build scripts #### Function Prototype ::: tip API ```lua before_buildcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before build script function with package and batchcmds parameters | Through this interface, we can configure pre-build scripts. ```lua xpack("test") set_formats("srpm") before_buildcmd(function (package, batchcmds) -- TODO end) ``` ## after\_buildcmd * Customize the script after the build #### Function Prototype ::: tip API ```lua after_buildcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After build script function with package and batchcmds parameters | Through this interface, we can configure the script after the build. ```lua xpack("test") set_formats("srpm") after_buildcmd(function (package, batchcmds) -- TODO end) ``` ## before\_installcmd * Add script before installation #### Function Prototype ::: tip API ```lua before_installcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before install script function with package and batchcmds parameters | It will not rewrite the entire installation script, but will add some custom installation scripts before the existing installation scripts are executed: ```lua xpack("test") before_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) ``` It should be noted that the cp, mkdir and other commands added through `batchcmds` will not be executed immediately, but will only generate a command list. When the package is actually generated later, these commands will be translated into packaging commands. ## on\_installcmd * Custom installation script #### Function Prototype ::: tip API ```lua on_installcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Install script function with package and batchcmds parameters | This time, the built-in default installation script is completely rewritten, including the internal automatic installation of files configured with `add_installfiles`. Users need to handle all the installation logic by themselves. ## after\_installcmd * Add post-installation scripts #### Function Prototype ::: tip API ```lua after_installcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After install script function with package and batchcmds parameters | It will not rewrite the entire installation script, but will add some custom installation scripts after the existing installation scripts are executed: ```lua xpack("test") after_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) ``` It should be noted that the cp, mkdir and other commands added through `batchcmds` will not be executed immediately, but will only generate a command list. When the package is actually generated later, these commands will be translated into packaging commands. ## before\_uninstallcmd * Add script before uninstallation #### Function Prototype ::: tip API ```lua before_uninstallcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Before uninstall script function with package and batchcmds parameters | Similar to before\_installcmd, please refer to before\_installcmd description. ## on\_uninstallcmd * Custom uninstall script #### Function Prototype ::: tip API ```lua on_uninstallcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | Uninstall script function with package and batchcmds parameters | Similar to on\_installcmd, please refer to on\_installcmd description. ## after\_uninstallcmd * Add script after uninstallation #### Function Prototype ::: tip API ```lua after_uninstallcmd(script: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | script | After uninstall script function with package and batchcmds parameters | Similar to after\_installcmd, please refer to after\_installcmd description. ## set\_nsis\_displayicon * Set the display icon of NSIS #### Function Prototype ::: tip API ```lua set_nsis_displayicon(iconfile: ) ``` ::: #### Parameter Description | Parameter | Description | |-----------|-------------| | iconfile | Icon file path string | This is an NSIS proprietary API that can be used to configure NSIS display icons: ```lua xpack("test") set_nsis_displayicon("bin/foo.exe") ``` We need to configure the executable file path with an icon so that the icon displayed in the installation package is consistent with it. This is an optional configuration. Even if we do not configure it, xmake will use the icon in the executable file in the associated target by default. --- --- url: /zh/api/description/xpack-component-interfaces.md --- # XPack 组件接口 {#xpack-component-interfaces} ## set\_title * 设置包组件的简单描述 #### 函数原型 ::: tip API ```lua set_title(title: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | title | 组件标题字符串 | #### 用法说明 ```lua xpack_component("LongPath") set_title("Enable Long Path") ``` ## set\_description * 设置包组件的详细描述 #### 函数原型 ::: tip API ```lua set_description(description: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | description | 组件详细描述字符串 | #### 用法说明 ```lua xpack_component("LongPath") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") ``` ## set\_default * 设置包组件的默认启用状态 #### 函数原型 ::: tip API ```lua set_default(default: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | default | 是否默认启用(布尔值) | #### 用法说明 通常包组件都会被默认启用,但是我们也可以使用这个接口,默认禁用这个组件,仅仅当用户安装包时候,选择勾选此组件,才会被启用安装。 ```lua xpack_component("LongPath") set_default(false) set_title("Enable Long Path") ``` ## on\_load * 自定义加载脚本 #### 函数原型 ::: tip API ```lua on_load(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 加载脚本函数,参数为component | #### 用法说明 自定义加载脚本,用于实现特定的组件配置逻辑。 我们可以在 on\_load 自定义脚本域中,进一步灵活的配置包组件。 ```lua xpack_component("test") on_load(function (component) local package = component:package() -- TODO end) ``` ## before\_installcmd * 添加组件安装之前的脚本 #### 函数原型 ::: tip API ```lua before_installcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装前脚本函数,参数为component和batchcmds | 它不会重写整个安装脚本,但是会在现有的安装脚本执行之前,新增一些自定义的安装脚本: ```lua xpack_component("test") before_installcmd(function (component, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) ``` 需要注意的是,通过 `batchcmds` 添加的 cp, mkdir 等命令都不会被立即执行,而是仅仅生成一个命令列表,后面实际生成包的时候,会将这些命令,翻译成打包命令。 它跟 xpack 的 before\_installcmd 使用是完全一样的,唯一的区别是,仅仅当这个组件被启用时候,才会执行这里的安装脚本。 ## on\_installcmd * 重写组件的安装脚本 #### 函数原型 ::: tip API ```lua on_installcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装脚本函数,参数为component和batchcmds | #### 用法说明 自定义安装脚本,用于实现特定的组件安装逻辑。 这会重写整个组件的安装脚本,类似 xpack 的 on\_installcmd。 ```lua xpack_component("test") on_installcmd(function (component, batchcmds) -- TODO end) ``` ## after\_installcmd * 添加组件安装之后的脚本 #### 函数原型 ::: tip API ```lua after_installcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装后脚本函数,参数为component和batchcmds | #### 用法说明 在组件安装完成后执行的自定义脚本,用于后处理操作。 在组件安装之后,会执行这里的脚本,类似 xpack 的 after\_installcmd。 ```lua xpack_component("test") after_installcmd(function (component, batchcmds) -- TODO end) ``` ## before\_uninstallcmd * 添加组件卸载之前的脚本 #### 函数原型 ::: tip API ```lua before_uninstallcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载前脚本函数,参数为component和batchcmds | 在组件安装之后,会执行这里的脚本,类似 xpack 的 before\_uninstallcmd。 ```lua xpack_component("test") before_uninstallcmd(function (component, batchcmds) -- TODO end) ``` ## on\_uninstallcmd * 重写组件卸载的脚本 #### 函数原型 ::: tip API ```lua on_uninstallcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载脚本函数,参数为component和batchcmds | #### 用法说明 自定义卸载脚本,用于实现特定的组件卸载逻辑。 这会重写整个组件的卸载脚本,类似 xpack 的 on\_uninstallcmd。 ```lua xpack_component("test") on_uninstallcmd(function (component, batchcmds) -- TODO end) ``` ## after\_uninstallcmd * 添加组件卸载之后的脚本 #### 函数原型 ::: tip API ```lua after_uninstallcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载后脚本函数,参数为component和batchcmds | #### 用法说明 在组件卸载完成后执行的自定义脚本,用于后处理操作。 在组件卸载之后,会执行这里的脚本,类似 xpack 的 before\_uninstallcmd。 ```lua xpack_component("test") before_uninstallcmd(function (component, batchcmds) -- TODO end) ``` ## add\_sourcefiles * 添加组件源文件 #### 函数原型 ::: tip API ```lua add_sourcefiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | files | 源文件模式字符串或数组 | | ... | 可变参数,可传递多个文件模式 | | prefixdir | 安装前缀目录 | | rootdir | 源文件根目录 | | filename | 目标文件名 | 这类似于 xpack 的 add\_sourcefiles,但这里仅仅当组件被启用后,才会将这些源文件打入安装包。 ## add\_installfiles * 添加组件二进制安装文件 #### 函数原型 ::: tip API ```lua add_installfiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | files | 安装文件模式字符串或数组 | | ... | 可变参数,可传递多个文件模式 | | prefixdir | 安装前缀目录 | | rootdir | 源文件根目录 | | filename | 目标文件名 | #### 用法说明 这类似于 xpack 的 add\_installfiles,但这里仅仅当组件被启用后,才会将这些二进制文件打入安装包。 --- --- url: /guide/package-management/xrepo-cli.md --- # Xrepo CLI ## Introduction Xrepo is a cross-platform C/C++ package manager based on [Xmake](https://github.com/xmake-io/xmake). It is based on the runtime provided by xmake, but it is a complete and independent package management program. Compared with package managers such as vcpkg/homebrew, xrepo can provide C/C++ packages for more platforms and architectures at the same time. And it also supports multi-version semantic selection. In addition, it is also a decentralized distributed repository. It not only provides the official [xmake-repo](https://github.com/xmake-io/xmake-repo) repository, it also supports users to build multiple private repositories. At the same time, xrepo also supports installing packages from third-party package managers such as vcpkg/homebrew/conan, and provides unified and consistent library link information to facilitate integration and docking with third-party projects. If you want to know more, please refer to: [Documents](https://xmake.io), [Github](https://github.com/xmake-io/xrepo) and [Gitee](https://gitee.com/tboox/xrepo). You can also [search packages here](https://xmake.microblock.cc). ![](https://github.com/xmake-io/xrepo-docs/raw/master/assets/img/xrepo.gif) ## Installation We only need to install xmake to use the xrepo command. About the installation of xmake, we can see: [Xmake Installation Document](/guide/quick-start.html#installation). ## Supported platforms * Windows (x86, x64) * macOS (i386, x86\_64, arm64) * Linux (i386, x86\_64, cross-toolchains, ...) * \*BSD (i386, x86\_64) * Android (x86, x86\_64, armeabi, armeabi-v7a, arm64-v8a) * iOS (armv7, armv7s, arm64, i386, x86\_64) * MSYS (i386, x86\_64) * MinGW (i386, x86\_64, arm, arm64) * Cross Toolchains ## Support distributed repository ::: tip Difference from xmake repo **`xrepo` CLI is used for global repository management**, applicable to all projects. The `xmake repo` command is only used for local package repository management within the current project. If you need to manage repositories globally (add, remove, view repositories), you should use the `xrepo` command. ::: In addition to directly retrieving the installation package from the official repository: [xmake-repo](https://github.com/xmake-io/xmake-repo). We can also add any number of self-built repositories, and even completely isolate the external network, and only maintain the installation and integration of private packages on the company's internal network. Just use the following command to add your own repository address: ```sh $ xrepo add-repo myrepo https://github.com/mygroup/myrepo ``` We can also remove an added repository: ```sh $ xrepo rm-repo myrepo ``` Or view all added global repositories: ```sh $ xrepo list-repo ``` ## Seamless integration with xmake project ```lua add_requires("tbox >1.6.1", "libuv master", "vcpkg::ffmpeg", "brew::pcre2/libpcre2-8") add_requires("conan::openssl/1.1.1g", {alias = "openssl", optional = true, debug = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("tbox", "libuv", "vcpkg::ffmpeg", "brew::pcre2/libpcre2-8", "openssl") ``` The following is the overall architecture and compilation process integrated with xmake. ## Get started ### Installation package #### Basic usage ```sh $ xrepo install zlib tbox ``` #### Install the specified version package ```sh $ xrepo install "zlib 1.2.x" $ xrepo install "zlib >=1.2.0" ``` #### Install the specified platform package ```sh $ xrepo install -p iphoneos -a arm64 zlib $ xrepo install -p android [--ndk=/xxx] zlib $ xrepo install -p mingw [--mingw=/xxx] zlib $ xrepo install -p cross --sdk=/xxx/arm-linux-musleabi-cross zlib ``` #### Install the debug package ```sh $ xrepo install -m debug zlib ``` #### Install the package with a dynamic library ```sh $ xrepo install -k shared zlib ``` #### Install the specified configuration package ```sh $ xrepo install -f "vs_runtime='MD'" zlib $ xrepo install -f "regex=true,thread=true" boost ``` #### Install packages from a third-party package manager ```sh $ xrepo install brew::zlib $ xrepo install vcpkg::zlib $ xrepo install conan::zlib/1.2.11 ``` ### Find the library information of the package ```sh $ xrepo fetch pcre2 { { linkdirs = { "/usr/local/Cellar/pcre2/10.33/lib" }, links = { "pcre2-8" }, defines = { "PCRE2_CODE_UNIT_WIDTH=8" }, includedirs = "/usr/local/Cellar/pcre2/10.33/include" } } ``` ```sh $ xrepo fetch --ldflags openssl -L/Users/ruki/.xmake/packages/o/openssl/1.1.1/d639b7d6e3244216b403b39df5101abf/lib -lcrypto -lssl ``` ```sh $ xrepo fetch --cflags openssl -I/Users/ruki/.xmake/packages/o/openssl/1.1.1/d639b7d6e3244216b403b39df5101abf/include ``` ```sh $ xrepo fetch -p [iphoneos|android] --cflags "zlib 1.2.x" -I/Users/ruki/.xmake/packages/z/zlib/1.2.11/df72d410e7e14391b1a4375d868a240c/include ``` ```sh $ xrepo fetch --cflags --ldflags conan::zlib/1.2.11 -I/Users/ruki/.conan/data/zlib/1.2.11/_/_/package/f74366f76f700cc6e991285892ad7a23c30e6d47/include -L/Users/ruki/.conan/data/zlib/1.2.11/_/_/package/f74366f76f700cc6e991285892ad7a23c30e6d47/lib -lz ``` ### Export the installed packages xrepo can quickly export installed packages, including corresponding library files, header files, etc. ```sh $ xrepo export -o /tmp/output zlib ``` ### Search supported packages ```sh $ xrepo search zlib "pcr*" zlib: -> zlib: A Massively Spiffy Yet Delicately Unobtrusive Compression Library (in xmake-repo) pcr*: -> pcre2: A Perl Compatible Regular Expressions Library (in xmake-repo) -> pcre: A Perl Compatible Regular Expressions Library (in xmake-repo) ``` ### Show package environment information ```sh $ xrepo env --show luajit { OLDPWD = "/mnt/tbox", HOME = "/home/ruki", PATH = "/home/ruki/.xmake/packages/l/luajit/2.1.0-beta3/fbac76d823b844f0b91abf3df0a3bc61/bin:/tmp:/tmp/arm-linux-musleabi-cross/bin:~/.local/bin: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", TERM = "xterm", PWD = "/mnt/xmake", XMAKE_PROGRAM_DIR = "/mnt/xmake/xmake", HOSTNAME = "e6edd61ff1ab", LD_LIBRARY_PATH = "/home/ruki/.xmake/packages/l/luajit/2.1.0-beta3/fbac76d823b844f0b91abf3df0a3bc61/lib", SHLVL = "1", _ = "/mnt/xmake/scripts/xrepo.sh" } ``` ### Package virtual environment #### Enter the virtual environment We can customize some package configurations by adding the xmake.lua file in the current directory, and then enter the specific package virtual environment. ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```sh $ xrepo env shell > python --version > luajit --version ``` We can also configure and load the corresponding toolchain environment in xmake.lua, for example, load the VS compilation environment. ```lua set_toolchains("msvc") ``` #### Manage virtual environments We can use the following command to register the specified virtual environment configuration globally to the system for quick switching. ```sh $ xrepo env --add /tmp/base.lua ``` At this time, we have saved a global virtual environment called base, and we can view it through the list command. ```sh $ xrepo env --list /Users/ruki/.xmake/envs: -base envs(1) found! ``` We can also delete it. ```sh $ xrepo env --remove base ``` #### Switch global virtual environment If we register multiple virtual environments, we can also switch them quickly. ```sh $ xrepo env -b base shell > python --version ``` Or directly load the specified virtual environment to run specific commands ```sh $ xrepo env -b base python --version ``` `xrepo env -b/--bind` is to bind the specified virtual environment. For more details, see: [#1762](https://github.com/xmake-io/xmake/issues/1762) #### Quick switching of temporary virtual environments Not only can we manage switching environments by configuring environment configuration files such as `myenv.lua`, but we can also specify a list of environment packages to bind to on the command line on an ad hoc basis, allowing for quick switching without any configuration. For example, if we want to enter an environment with python 3.0, luajit and cmake, all we need to do is to execute ```sh $ xrepo env -b "python 3.x,luajit,cmake" shell [python,luajit,cmake] $ python --version Python 3.10.6 [python,luajit,cmake] $ cmake --version cmake version 3.25.3 ``` Xmake will automatically install the dependencies and open a new shell environment, which will also have a prompt on the left side of the terminal. If we want to exit the current environment, we simply need to run ```sh [python,luajit,cmake] $ xrepo env quit $ ``` ### Show the given package information ```sh $ xrepo info zlib The package info of project: require(zlib): -> description: A Massively Spiffy Yet Delicately Unobtrusive Compression Library -> version: 1.2.11 -> urls: -> http://zlib.net/zlib-1.2.11.tar.gz -> c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 -> https://downloads.sourceforge.net/project/libpng/zlib/1.2.11/zlib-1.2.11.tar.gz -> c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 -> repo: xmake-repo https://gitee.com/tboox/xmake-repo.git master -> cachedir: /Users/ruki/.xmake/cache/packages/2010/z/zlib/1.2.11 -> installdir: /Users/ruki/.xmake/packages/z/zlib/1.2.11/d639b7d6e3244216b403b39df5101abf -> searchdirs: -> searchnames: zlib-1.2.11.tar.gz -> fetchinfo: 1.2.11, system -> version: 1.2.11 -> links: z -> linkdirs: /usr/local/Cellar/zlib/1.2.11/lib -> includedirs: /usr/local/Cellar/zlib/1.2.11/include -> platforms: iphoneos, mingw@windows, macosx, mingw@linux,macosx, android@linux,macosx, windows, linux -> requires: -> plat: macosx -> arch: x86_64 -> configs: -> debug: false -> vs_runtime: MT -> shared: false -> configs: -> configs (builtin): -> debug: Enable debug symbols. (default: false) -> shared: Enable shared library. (default: false) -> cflags: Set the C compiler flags. -> cxflags: Set the C/C++ compiler flags. -> cxxflags: Set the C++ compiler flags. -> asflags: Set the assembler flags. -> vs_runtime: Set vs compiler runtime. (default: MT) -> values: {"MT","MD"} ``` ### Download package source code We can use the `xrepo download` command to only download the source code of the specified package without installing it. ```sh $ xrepo download zlib $ xrepo download "zlib 2.x" ``` The default download directory is in the `packages` subdirectory of the current directory. We can also modify the download directory through the `-o outputdir` parameter. ```sh $ xrepo download -o /tmp zlib ``` --- --- url: /zh/guide/package-management/xrepo-cli.md --- # Xrepo CLI ## 简介 Xrepo 是一个基于 [Xmake](https://github.com/xmake-io/xmake) 的跨平台 C/C++ 包管理器。 它基于 xmake 提供的运行时,但却是一个完整独立的包管理程序。相比 vcpkg、homebrew 等包管理器,xrepo 能够同时支持更多平台和架构的 C/C++ 包。 同时还支持多版本语义选择。此外,它还是一个去中心化的分布式仓库,不仅提供官方的 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库,还支持用户自建多个私有仓库。 此外,xrepo 也支持从 vcpkg、homebrew、conan 等第三方包管理器中安装包,并提供统一一致的库链接信息,方便与第三方项目集成。 如果你想了解更多,请参考:[在线文档](https://xmake.io/zh/)、[Github](https://github.com/xmake-io/xrepo) 以及 [Gitee](https://gitee.com/tboox/xrepo) ![](https://github.com/xmake-io/xrepo-docs/raw/master/assets/img/xrepo.gif) ## 安装 只需安装 xmake 即可使用 xrepo 命令。关于 xmake 的安装,请参见:[Xmake 安装文档](/zh/guide/quick-start.html#installation)。 ## 支持平台 * Windows (x86, x64) * macOS (i386, x86\_64, arm64) * Linux (i386, x86\_64, cross-toolchains ..) * \*BSD (i386, x86\_64) * Android (x86, x86\_64, armeabi, armeabi-v7a, arm64-v8a) * iOS (armv7, armv7s, arm64, i386, x86\_64) * MSYS (i386, x86\_64) * MinGW (i386, x86\_64, arm, arm64) * Cross Toolchains ## 分布式仓库支持 ::: tip 与 xmake repo 的区别 **`xrepo` CLI 用于全局管理仓库**,适用于所有项目。而 `xmake repo` 命令仅用于当前工程下的本地包仓库管理。 如果需要全局管理仓库(添加、删除、查看仓库),应该使用 `xrepo` 命令。 ::: 除了可以直接从官方仓库:[xmake-repo](https://github.com/xmake-io/xmake-repo) 检索安装包之外, 我们还可以添加任意多个自建仓库,甚至可以完全隔离外网,仅在公司内部网络维护私有包的安装集成。 只需通过下面的命令,添加自己的仓库地址: ```sh $ xrepo add-repo myrepo https://github.com/mygroup/myrepo ``` 我们也可以移除已添加的仓库: ```sh $ xrepo rm-repo myrepo ``` 或者查看所有已添加的全局仓库: ```sh $ xrepo list-repo ``` ## 与 xmake 的工程无缝集成 ```lua add_requires("tbox >1.6.1", "libuv master", "vcpkg::ffmpeg", "brew::pcre2/libpcre2-8") add_requires("conan::openssl/1.1.1g", {alias = "openssl", optional = true, debug = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("tbox", "libuv", "vcpkg::ffmpeg", "brew::pcre2/libpcre2-8", "openssl") ``` 下面是与 xmake 集成的整体架构和编译流程示意图。 ## 快速上手 ### 安装包 #### 基本使用 ```sh $ xrepo install zlib tbox ``` #### 安装指定版本包 完全支持 Semantic Versioning(语义版本)。 ```sh $ xrepo install "zlib 1.2.x" $ xrepo install "zlib >=1.2.0" ``` #### 安装指定平台包 ```sh $ xrepo install -p iphoneos -a arm64 zlib $ xrepo install -p android [--ndk=/xxx] zlib $ xrepo install -p mingw [--mingw=/xxx] zlib $ xrepo install -p cross --sdk=/xxx/arm-linux-musleabi-cross zlib ``` #### 安装调试版本包 ```sh $ xrepo install -m debug zlib ``` #### 安装动态库版本包 ```sh $ xrepo install -k shared zlib ``` #### 安装指定配置包 ```sh $ xrepo install -f "vs_runtime='MD'" zlib $ xrepo install -f "regex=true,thread=true" boost ``` #### 安装第三方包管理器的包 ```sh $ xrepo install brew::zlib $ xrepo install vcpkg::zlib $ xrepo install conan::zlib/1.2.11 ``` ### 查找包的库使用信息 ```sh $ xrepo fetch pcre2 { { linkdirs = { "/usr/local/Cellar/pcre2/10.33/lib" }, links = { "pcre2-8" }, defines = { "PCRE2_CODE_UNIT_WIDTH=8" }, includedirs = "/usr/local/Cellar/pcre2/10.33/include" } } ``` ```sh $ xrepo fetch --ldflags openssl -L/Users/ruki/.xmake/packages/o/openssl/1.1.1/d639b7d6e3244216b403b39df5101abf/lib -lcrypto -lssl ``` ```sh $ xrepo fetch --cflags openssl -I/Users/ruki/.xmake/packages/o/openssl/1.1.1/d639b7d6e3244216b403b39df5101abf/include ``` ```sh $ xrepo fetch -p [iphoneos|android] --cflags "zlib 1.2.x" -I/Users/ruki/.xmake/packages/z/zlib/1.2.11/df72d410e7e14391b1a4375d868a240c/include ``` ```sh $ xrepo fetch --cflags --ldflags conan::zlib/1.2.11 -I/Users/ruki/.conan/data/zlib/1.2.11/_/_/package/f74366f76f700cc6e991285892ad7a23c30e6d47/include -L/Users/ruki/.conan/data/zlib/1.2.11/_/_/package/f74366f76f700cc6e991285892ad7a23c30e6d47/lib -lz ``` ### 导出安装后的包 xrepo 可以快速导出已安装的包,包括对应的库文件、头文件等。 ```sh $ xrepo export -o /tmp/output zlib ``` ### 搜索支持的包 ```sh $ xrepo search zlib "pcr*" zlib: -> zlib: A Massively Spiffy Yet Delicately Unobtrusive Compression Library (in xmake-repo) pcr*: -> pcre2: A Perl Compatible Regular Expressions Library (in xmake-repo) -> pcre: A Perl Compatible Regular Expressions Library (in xmake-repo) ``` ### 查看包环境信息 ```sh $ xrepo env --show luajit { OLDPWD = "/mnt/tbox", HOME = "/home/ruki", PATH = "/home/ruki/.xmake/packages/l/luajit/2.1.0-beta3/fbac76d823b844f0b91abf3df0a3bc61/bin:/tmp:/tmp/arm-linux-musleabi-cross/bin:~/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", TERM = "xterm", PWD = "/mnt/xmake", XMAKE_PROGRAM_DIR = "/mnt/xmake/xmake", HOSTNAME = "e6edd61ff1ab", LD_LIBRARY_PATH = "/home/ruki/.xmake/packages/l/luajit/2.1.0-beta3/fbac76d823b844f0b91abf3df0a3bc61/lib", SHLVL = "1", _ = "/mnt/xmake/scripts/xrepo.sh" } ``` ### 包虚拟环境 #### 进入虚拟环境 我们可以通过在当前目录下,添加 xmake.lua 文件,定制化一些包配置,然后进入特定的包虚拟环境。 ```lua add_requires("zlib 1.2.11") add_requires("python 3.x", "luajit") ``` ```sh $ xrepo env shell > python --version > luajit --version ``` 我们也可以在 xmake.lua 配置加载对应的工具链环境,比如加载 vs 的编译环境。 ```lua set_toolchains("msvc") ``` #### 管理虚拟环境 我们可以使用下面的命令,把指定的虚拟环境配置全局注册到系统中,方便快速切换。 ```sh $ xrepo env --add /tmp/base.lua ``` 此时,我们就保存了一个名为 base 的全局虚拟环境,可以通过 list 命令查看。 ```sh $ xrepo env --list /Users/ruki/.xmake/envs: - base envs(1) found! ``` 我们也可以删除它。 ```sh $ xrepo env --remove base ``` #### 切换全局虚拟环境 如果我们注册了多个虚拟环境,也可以快速切换它们。 ```sh $ xrepo env -b base shell > python --version ``` 或者直接加载指定虚拟环境运行特定命令。 ```sh $ xrepo env -b base python --version ``` `xrepo env -b/--bind` 就是绑定指定的虚拟环境,更多详情见:[#1762](https://github.com/xmake-io/xmake/issues/1762) #### 快速切换临时虚拟环境 我们不仅可以通过配置 `myenv.lua` 等环境配置文件,来管理切换环境,也可以直接在命令行临时指定需要绑定的环境包列表,实现快速切换,无需任何配置。 例如,我们想进入一个带有 python 3.0, luajit 和 cmake 的环境,只需要执行: ```sh $ xrepo env -b "python 3.x,luajit,cmake" shell [python,luajit,cmake] $ python --version Python 3.10.6 [python,luajit,cmake] $ cmake --version cmake version 3.25.3 ``` Xmake 会自动安装相关依赖,然后开启一个新的 shell 环境,新环境终端左边也有 prompt 提示。 如果我们想退出当前环境,仅仅需要执行 ```sh [python,luajit,cmake] $ xrepo env quit $ ``` ### 查看包信息 ```sh $ xrepo info zlib The package info of project: require(zlib): -> description: A Massively Spiffy Yet Delicately Unobtrusive Compression Library -> version: 1.2.11 -> urls: -> http://zlib.net/zlib-1.2.11.tar.gz -> c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 -> https://downloads.sourceforge.net/project/libpng/zlib/1.2.11/zlib-1.2.11.tar.gz -> c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 -> repo: xmake-repo https://gitee.com/tboox/xmake-repo.git master -> cachedir: /Users/ruki/.xmake/cache/packages/2010/z/zlib/1.2.11 -> installdir: /Users/ruki/.xmake/packages/z/zlib/1.2.11/d639b7d6e3244216b403b39df5101abf -> searchdirs: -> searchnames: zlib-1.2.11.tar.gz -> fetchinfo: 1.2.11, system -> version: 1.2.11 -> links: z -> linkdirs: /usr/local/Cellar/zlib/1.2.11/lib -> includedirs: /usr/local/Cellar/zlib/1.2.11/include -> platforms: iphoneos, mingw@windows, macosx, mingw@linux,macosx, android@linux,macosx, windows, linux -> requires: -> plat: macosx -> arch: x86_64 -> configs: -> debug: false -> vs_runtime: MT -> shared: false -> configs: -> configs (builtin): -> debug: Enable debug symbols. (default: false) -> shared: Enable shared library. (default: false) -> cflags: Set the C compiler flags. -> cxflags: Set the C/C++ compiler flags. -> cxxflags: Set the C++ compiler flags. -> asflags: Set the assembler flags. -> vs_runtime: Set vs compiler runtime. (default: MT) -> values: {"MT","MD"} ``` ### 下载包源码 我们可以使用 `xrepo download` 命令,仅仅下载指定包的源码,而不去安装它。 ```sh $ xrepo download zlib $ xrepo download "zlib 2.x" ``` 默认的下载目录就是当前的目录下 `packages` 子目录中,我们也可以通过 `-o outputdir` 参数修改下载目录。 ```sh $ xrepo download -o /tmp zlib ``` --- --- url: /zh/posts/precompiled-header.md --- 最近为了给[xmake](https://xmake.io)实现预编译头文件的支持,研究了下各大主流编译器处理预编译头的机制以及之间的一些差异。 现在的大部分c/c++编译器都是支持预编译头的,例如:gcc,clang,msvc等,用于优化c++代码的编译速度,毕竟c++的头文件如果包含了模板定义的话,编译速度是很慢的, 如果能够吧大部分通用的头文件放置在一个`header.h`中,在其他源码编译之前预先对其进行编译,之后的代码都能重用这部分预编译头,就可以极大程度上减少频繁的头文件冗余编译。 但是不同编译器对它的支持力度和处理方式,还是有很大差异的,并不是非常通用,在xmake中封装成统一的接口和使用方式,还是费了很大的功夫才搞定。 #### msvc的预编译头处理 预编译头在msvc的项目中很常见,经常会看到类似`stdafx.cpp`, `stdafx.h`的文件,就是用于此目的,而msvc编译器是通过编译`stdafx.cpp`来生成预编译头文件`stdafx.pch`的。 创建预编译头的命令行如下: ```bash $ cl.exe -c -Yc -Fpstdafx.pch -Fostdafx.obj stdafx.cpp ``` 其中,`-Yc`就是创建预编译头`stdafx.pch`的意思,通过`-Fp`来指定`*.pch`的输出文件路径,用`-Fo`指定编译`stdafx.cpp`生成对象文件。 其他源码是如何使用这个`stdafx.pch`的呢,通过将`stdafx.h`传入`-Yu`来告诉编译器,编译当前代码,忽略`#include "stdafx.h"`,直接使用已经编译好的`stdafx.pch`文件。 ```bash $ cl.exe -c -Yustdafx.h -Fpstdafx.pch -Fotest.obj test.cpp ``` 最后链接的时候,需要把:`stdafx.obj`, `test.obj`都连接上才行,这个也是和gcc, clang编译器不同的地方。 ```bash $ link.exe -out:test test.obj stdafx.obj ``` 注:一定要吧`stdafx.obj`也链接上哦,虽然`stdafx.cpp`仅用于生成`stdafx.pch`,但是对象文件也是需要。 还有个跟gcc, clang有区别的地方是,msvc的`-Yu`指定`stdafx.h`必须是`#include "stdafx.h"`中的头文件名字,不是文件路径哦。 #### clang的预编译头文件处理 个人感觉clang的预编译头文件支持方式最为友好,也最为简单。 相比于msvc,不需要`stdafx.cpp`,只需要一个头文件`stdafx.h`就可以生成pch文件。 相比于gcc,可以灵活控制pch文件的路径,更加灵活。 编译头文件生成pch文件: ```bash $ clang -c -o stdafx.pch stdafx.h ``` 使用预编译头文件: ```bash $ clang -c -include stdafx.h -include-pch stdafx.pch -o test.o test.cpp ``` 其中`-include stdafx.h`用于忽略编译`test.cpp`中的`#include "stdafx.h"`,通过`-include-pch`使用预先编译好的`stdafx.pch`。 并且这里指定的`stdafx.h`和`stdafx.pch`不仅可以是在includedir搜索路径下的文件,也可以指定全路径文件名,非常灵活,例如: ```bash $ clang -c -include inc/stdafx.h -include-pch out/stdafx.pch -o test.o test.cpp ``` #### gcc的预编译头文件处理 gcc的预编译头处理方式基本上跟clang的类似,唯一的区别就是:它不支持`-include-pch`参数,因此不能所以指定使用的`stdafx.pch`文件路径。 它有自己的搜索规则: 1. 从`stdafx.h`所在目录中,查找`stdafx.h.pch`文件是否存在 2. 从`-I`的头文件搜索路径找查找`stdafx.h.pch` 编译头文件生成pch文件: ```bash $ gcc -c -o stdafx.pch stdafx.h ``` 使用预编译头文件: ```bash $ gcc -c -include stdafx.h -o test.o test.cpp ``` 为了让上述代码正常编译,`stdafx.h.pch`必须放置在`stdafx.h`的相同目录下,这样编译才能找到,目前我还没有找到可以所以指定输出目录的方法。 #### 其他注意点 gcc、clang对于`*.h`的头文件编译,默认是作为c预编译头来使用的,这跟c++的pch是不一样的,无法给c++的代码使用,如果要生成c++可用的pch文件,必须要告诉编译器,如何去编译`stdafx.h`。 这个可以通过`-x c++-header`参数来解决: ```bash $ gcc -c -x c++-header -o stdafx.pch stdafx.h ``` 当然也可以通过修改后缀名来解决: ```bash $ gcc -c -o stdafx.pch stdafx.hpp ``` #### xmake对预编译头文件的支持 xmake支持通过预编译头文件去加速`c/c++`程序编译,目前支持的编译器有:gcc, clang和msvc。 对于c预编译头文件的使用方式如下: ```lua target("test") set_pcheader("header.h") ``` 如果是对c++头文件的预编译,改成: ```lua target("test") set_pcxxheader("header.h") ``` 更多使用说明见:[target.set\_pcheader](https://xmake.io/zh/) #### 参考资料 [Speed up C++ compilation, part 1: precompiled headers](https://xmake.io/zh/) --- --- url: /zh/guide/extensions/theme-style.md --- # 主题风格 {#theme-style} ## 切换主题 {#switch-theme} 如果用户不喜欢 xmake 默认的显示配色和风格,我们可以通过下面的全局配置命令,切换到 xmake 提供的其他一些配置主题,例如: ```sh $ xmake g --theme=dark ``` 默认的主题名为 default,这里我们通过切换到 dark 风格主题,来适配一些终端背景很浅的场景,提供更加深色的色彩输出,避免看不清。 如果我们要切回默认主题,可以直接输入: ```sh $ xmake g -c ``` 或者 ```sh $ xmake g --theme=default ``` 另外,xmake 还提供了不少有趣实用的内置主题,大家可以试试,下面会详细讲解。 ::: tip 注意 如果大家有更好的主题,也欢迎提pr贡献进来,非常感谢! ::: ## 内置主题 {#builtin-themes} ### 默认主题 {#default-theme} 这个也就是咱们安装xmake后默认的显示主题,主题名:default,它默认会提供色彩输出,适合一些深色背景的终端。 我们也可以通过下面的命令切回默认主题: ```sh $ xmake g --theme=default ``` ### Ninja 主题 {#ninja-theme} 这个是 v2.3.4 之后版本提供的主题,构建进度风格类似 ninja,采用单行进度条,不再回滚进度,用户可以根据自己的喜好设置。 除了进度展示不同外,其他都跟默认主题的配置相同。 ```sh $ xmake g --theme=ninja ``` ### Emoji 主题 {#emoji-theme} 这个主题部分输出通过 emoji 字符代替之前的色彩输出。 ```sh $ xmake g --theme=emoji ``` ### Dark 主题 {#dark-theme} 这个主题主要是针对一些终端背景为浅色系(比如淡黄色等),导致一些警告输出(默认也是黄色)重合不可见,所以把主题配色变成深色系,提高可见性。 ```sh $ xmake g --theme=dark ``` ### Light 主题 {#light-theme} 这个主题主要是针对一些终端背景为深色系,导致一些输出重合不可见,所以把主题配色变成浅色系,提高可见性。 ```sh $ xmake g --theme=light ``` ### Plain 主题 {#plain-theme} 这个主题,其实就是完全禁用色彩、emoji 输出,主要是应对一些不支持 colors code 的终端导致显示乱码的问题,也是最朴素的主题风格。 ::: tip 注意 一些 Windows 终端可能不支持 colors,就可以设置这个主题来解决乱码显示问题。 ::: ```sh $ xmake g --theme=plain ``` ### Powershell 主题 {#powershell-theme} Windows 下 powershell 终端背景是蓝色的,而它的调色板配置似乎被改过,其中 magenta 色居然显示成背景蓝色,很诡异,导致 Xmake 的默认输出会有局部文本不可见(与背景色重叠了)。 所以,这个主题就是为了更好地适配 powershell 终端下的显示输出。 ```sh $ xmake g --theme=powershell ``` --- --- url: /zh/guide/basic-commands/cross-compilation.md --- # 交叉编译 {#cross-compilation} 通常,如果我们需要在当前pc环境编译生成其他设备上才能运行的目标文件时候,就需要通过对应的交叉编译工具链来编译生成它们,比如在win/macos上编译linux的程序,或者在linux上编译其他嵌入式设备的目标文件等。 通常的交叉编译工具链都是基于gcc/clang的,大都具有类似如下的结构: ``` /home/toolchains_sdkdir - bin - arm-linux-armeabi-gcc - arm-linux-armeabi-ld - ... - lib - libxxx.a - include - xxx.h ``` 每个工具链都有对应的include/lib目录,用于放置一些系统库和头文件,比如libc, stdc++等,而bin目录下放置的就是编译工具链一系列工具。例如: ``` arm-linux-armeabi-ar arm-linux-armeabi-as arm-linux-armeabi-c++ arm-linux-armeabi-cpp arm-linux-armeabi-g++ arm-linux-armeabi-gcc arm-linux-armeabi-ld arm-linux-armeabi-nm arm-linux-armeabi-strip ``` 其中`arm-linux-armeabi-`前缀就是cross,通过用来标示目标平台和架构,主要用于跟主机自身的gcc/clang进行区分。 里面的gcc/g++就是c/c++的编译器,通常也可以作为链接器使用,链接的时候内部会去调用ld来链接,并且自动追加一些c++库。 cpp是预处理器,as是汇编器,ar用于生成静态库,strip用于裁剪掉一些符号信息,使得目标程序会更小。nm用于查看导出符号列表。 ## 自动探测和编译 如果我们的交叉编译工具链是上述结构,xmake会自动检测识别这个sdk的结构,提取里面的cross,以便于include/lib路径位置,用户通常不需要做额外的参数设置,只需要配置好sdk根目录就可以编译了,例如: ```sh $ xmake f -p cross --sdk=/home/toolchains_sdkdir $ xmake ``` 其中,`-p cross`用于指定当前的平台是交叉编译平台,`--sdk=`用于指定交叉工具链的根目录。 注:我们也可以指定`-p linux`平台来配置交叉编译,效果是一样的,唯一的区别是额外标识了linux平台名,方便xmake.lua里面通过`is_plat("linux")`来判断平台。 此时,xmake会去自动检测gcc等编译器的前缀cross:`arm-linux-armeabi-`,并且编译的时候,也会自动加上`链接库`和`头文件`的搜索选项,比如: ``` -I/home/toolchains_sdkdir/include -L/home/toolchains_sdkdir/lib ``` 这些都是xmake自动处理的,不需要手动配置它们。 ## 手动配置编译 如果上面的自动检测对某些工具链,还无法完全通过编译,就需要用户自己手动设置一些交叉编译相关的配置参数,来调整适应这些特殊的工具链了,下面我会逐一讲解如何配置。 ## 设置工具链bin目录 对于不规则工具链目录结构,单纯地设置[--sdk](#sdk)选项还不够,可以继续通过这个选项设置工具链的bin目录位置。 例如:一些特殊的交叉工具链的,编译器bin目录,并不在 `/home/toolchains_sdkdir/bin` 这个位置,而是独立到了 `/usr/opt/bin` 这个时候,我们可以在设置了sdk参数的基础上追加bin目录的参数设置,来调整工具链的bin目录。 ```sh $ xmake f -p cross --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin $ xmake ``` ## 设置交叉工具链工具前缀 像aarch64-linux-android-,通常如果你配置了--sdk或者--bin,xmake会自动检测的,不需要自己手动设置。 但是对于一些极特殊的工具链,一个目录下同时有多个cross前缀的工具bin混在一起的情况,你需要手动设置这个配置,来区分到底需要选用哪一个bin。 例如,toolchains的bin目录下同时存在两个不同的编译器: ``` /opt/bin - armv7-linux-gcc - aarch64-linux-gcc ``` 我们现在想要选用armv7的版本,那么我们可以追加`--cross=`配置编译工具前缀名,例如: ```sh $ xmake f -p cross --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- ``` ## 设置c/c++编译器 如果还要继续细分选择编译器,则继续追加相关编译器选项,比如: ```sh $ xmake f -p cross --sdk=/user/toolsdk --cc=armv7-linux-clang --cxx=armv7-linux-clang++ ``` 注意:如果存在CC/CXX环境变量的话,会优先使用当前环境变量中指定的值。 如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么编译器工具检测就会失败。 这个时候我们可以通过: ```sh xmake f --cxx=clang++@/home/xxx/c++mips.exe ``` 设置c++mips.exe编译器作为类clang++的使用方式来编译。 也就是说,在指定编译器为`c++mips.exe`的同时,告诉xmake,它跟clang++用法和参数选项基本相同。 ## 设置c/c++链接器 如果还要继续细分选择链接器,则继续追加相关链接器选项,比如: ```sh $ xmake f -p cross --sdk=/user/toolsdk --ld=armv7-linux-clang++ --sh=armv7-linux-clang++ --ar=armv7-linux-ar ``` ld指定可执行程序链接器,sh指定共享库程序链接器,ar指定生成静态库的归档器。 注意:如果存在LD/SH/AR环境变量的话,会优先使用当前环境变量中指定的值。 ## 设置头文件和库搜索目录 如果sdk里面还有额外的其他include/lib目录不在标准的结构中,导致交叉编译找不到库和头文件,那么我们可以通过`--includedirs`和`--linkdirs`来追加搜索路径,然后通过`--links`添加额外的链接库。 ```sh $ xmake f -p cross --sdk=/usr/toolsdk --includedirs=/usr/toolsdk/xxx/include --linkdirs=/usr/toolsdk/xxx/lib --links=pthread ``` 注意:如果要指定多个搜索目录,可以通过`:`或者`;`来分割,也就是不同主机平台的路径分隔符,linux/macos下用`:`,win下用`;`。 ## 设置编译和链接选项 我们也可以根据实际情况通过`--cflags`, `--cxxflags`,`--ldflags`,`--shflags`和`--arflags`额外配置一些编译和链接选项。 * cflags: 指定c编译参数 * cxxflags:指定c++编译参数 * cxflags: 指定c/c++编译参数 * asflags: 指定汇编器编译参数 * ldflags: 指定可执行程序链接参数 * shflags: 指定动态库程序链接参数 * arflags: 指定静态库的生成参数 例如: ```sh $ xmake f -p cross --sdk=/usr/toolsdk --cflags="-DTEST -I/xxx/xxx" --ldflags="-lpthread" ``` ## 自定义编译平台 如果某个交叉工具链编译后目标程序有对应的平台需要指定,并且需要在xmake.lua里面根据不同交叉编译平台,还需要配置一些额外的编译参数,那么上文的`-p cross`设置就不能满足需求了。 通过这种方式,xmake就可以很方便的扩展处理各种编译平台,用户可以自己扩展支持freebsd, netbsd, sunos等其他各种平台的交叉编译。 我摘录一段之前移植libuv写的交叉编译的配置,直观感受下: ```lua -- for dragonfly/freebsd/netbsd/openbsd platform if is_plat("dragonfly", "freebsd", "netbsd", "openbsd") then add_files("src/unix/bsd-ifaddrs.c") add_files("src/unix/freebsd.c") add_files("src/unix/kqueue.c") add_files("src/unix/posix-hrtime.c") add_headerfiles("(include/uv-bsd.h)") end -- for sunos platform if is_plat("sunos") then add_files("src/unix/no-proctitle.c") add_files("src/unix/sunos.c") add_defines("__EXTENSIONS_", "_XOPEN_SOURCE=600") add_headerfiles("(include/uv-sunos.h)") end ``` 然后,我们就可以切换这些平台来编译: ```sh $ xmake f -p [dragonfly|freebsd|netbsd|openbsd|sunos] --sdk=/home/arm-xxx-gcc/ $ xmake ``` 另外,内置的linux平台也是支持交叉编译的哦,如果不想配置其他平台名,统一作为linux平台来交叉编译,也是可以的。 ```sh $ xmake f -p linux --sdk=/usr/local/arm-xxx-gcc/ $ xmake ``` 只要设置了`--sdk=`等参数,就会启用linux平台的交叉编译模式。 ## 常用工具链配置 完整的工具链列表,请执行下面的命令查看: ```sh $ xmake show -l toolchains ``` ::: tip 注意 此特性需要v2.3.4以上版本才支持 ::: 上文讲述的是通用的交叉编译工具链配置,如果一些特定的工具链需要额外传入`--ldflags/--includedirs`等场景就比较繁琐了, 因此xmake也内置了一些常用工具链,可以省去交叉编译工具链复杂的配置过程,只需要执行: ```sh $ xmake f --toolchain=gnu-rm --sdk=/xxx/ $ xmake ``` 就可以快速切换的指定的交叉编译工具链,如果这个工具链需要追加一些特定的flags设置,也会自动设置好,简化配置。 其中,gnu-rm就是内置的GNU Arm Embedded Toolchain。 比如,我们也可以快速从gcc工具链整体切换到clang或者llvm工具链,不再需要`xmake f --cc=clang --cxx=clang --ld=clang++`等挨个配置了。 ```sh $ xmake f --toolchain=clang $ xmake ``` 或者 ```sh $ xmake f --toolchain=llvm --sdk=/xxx/llvm $ xmake ``` 具体xmake支持哪些工具链,可以通过下面的命令查看: ```sh $ xmake show -l toolchains xcode Xcode IDE vs VisualStudio IDE yasm The Yasm Modular Assembler clang A C language family frontend for LLVM go Go Programming Language Compiler dlang D Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit ndk Android NDK rust Rust Programming Language Compiler llvm A collection of modular and reusable compiler and toolchain technologies cross Common cross compilation toolchain nasm NASM Assembler gcc GNU Compiler Collection mingw Minimalist GNU for Windows gnu-rm GNU Arm Embedded Toolchain envs Environment variables toolchain fasm Flat Assembler ``` ### 自定义工具链 另外,我们也可以在xmake.lua中自定义toolchain,然后通过`xmake f --toolchain=myclang`指定切换,例如: ```lua toolchain("myclang") set_kind("standalone") set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") -- ... ``` 关于这块的详情介绍,可以到[自定义工具链](/zh/api/description/custom-toolchain)章节查看 更多详情见:[#780](https://github.com/xmake-io/xmake/issues/780) ### MingW 工具链 使用mingw工具链编译,其实也是交叉编译,但是由于这个比较常用,xmake专门增加了一个mingw的平台来快速处理使用mingw工具链的编译。 因此,xmake对mingw的工具链检测会更加完善,在macos下,基本上连sdk路径都不需要配置,也能直接检测到,只需要切到mingw平台编译即可。 ```sh $ xmake f -p mingw $ xmake -v configure { ld = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ ndk_stdcxx = true plat = mingw mingw = /usr/local/opt/mingw-w64 buildir = build arch = x86_64 xcode = /Applications/Xcode.app mode = release cxx = /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc cross = x86_64-w64-mingw32- theme = default kind = static ccache = true host = macosx clean = true bin = /usr/local/opt/mingw-w64/bin } [ 0%]: cache compiling.release src/main.cpp /usr/local/bin/ccache /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-gcc -c -fvisibility=hidden -O3 -m64 -o build/.objs/test/mingw/x86_64/release/src/main.cpp.obj src/main.cpp [100%]: linking.release test.exe /usr/local/opt/mingw-w64/bin/x86_64-w64-mingw32-g++ -o build/mingw/x86_64/release/test.exe build/.objs/test/mingw/x86_64/release/src/main.cpp.obj -s -fvisibility=hidden -m64 build ok! ``` 这里我们追加了`-v`参数,看了下详细的编译命令和检测到的mingw工具链配置值,其中cross被自动检测为:`x86_64-w64-mingw32-`,bin目录也被自动检测到了,还有编译器和链接器也是。 尽管在linux/win上还没法自动检测到sdk路径,我们也可以手动指定sdk路径,需要注意的是,xmake为mingw专门提供了一个`--mingw=`参数用来指定mingw的工具链根目录,其效果跟`--sdk=`是一样的,但是它可以作为全局配置被设置。 ```sh $ xmake g --mingw=/home/mingwsdk $ xmake f -p mingw $ xmake ``` 我们通过`xmake g/global`命令设置`--mingw`根目录到全局配置后,之后每次编译和切换编译平台,就不用额外指定mingw工具链路径了,方便使用。 另外,其他的工具链配置参数用法,跟上文描述的没什么区别,像`--cross`, `--bin=`等都可以根据实际的环境需要,自己控制是否需要额外追加配置来适配自己的mingw工具链。 xmake 还支持 llvm-mingw 工具链,可以切换到 arm/arm64 架构来编译。 ```sh $ xmake f --mingw=/xxx/llvm-mingw -a arm64 $ xmake ``` ### LLVM 工具链 llvm工具链下载地址:https://releases.llvm.org/ ```sh $ xmake f -p cross --toolchain=llvm --sdk="C:\Program Files\LLVM" $ xmake ``` ### GNU-RM 工具链 工具链地址:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads# ```sh $ xmake f -p cross --toolchain=gnu-rm --sdk=/xxx/cc-arm-none-eabi-9-2019-q4-major $ xmake ``` ### TinyC 工具链 ```sh $ xmake f --toolchain=tinyc $ xmake ``` ::: tip 注意 Releases目录下,我们还提供了特殊的 xmake-tinyc-vX.X.X.win32.exe 安装包,内置tinyc工具链,无需依赖msvc,也可以编译c代码,开箱即用无依赖。 ::: ### Emcc 工具链 通常只需要切换到 Wasm 平台,里面内置了 emcc 工具链,还会额外调整目标程序的扩展名为 `*.html` 以及输出 `*.wasm`。 ```sh $ xmake f -p wasm $ xmake ``` 不过我们也能够直接切换到 emcc 工具链,但是后缀名不会被修改。 ```sh $ xmake f --toolchain=emcc $ xmake ``` ### Intel C++ 编译工具链 ```sh $ xmake f --toolchain=icc $ xmake ``` ### Intel Fortran 编译工具链 ```sh $ xmake f --toolchain=ifort $ 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`静态库归档器 | | [--scld](#-scld) | 设置`swift`链接器 | | [--scsh](#-scsh) | 设置`swift`共享库链接器 | | [--gcld](#-gcld) | 设置`golang`链接器 | | [--gcar](#-gcar) | 设置`golang`静态库归档器 | | [--dcld](#-dcld) | 设置`dlang`链接器 | | [--dcsh](#-dcsh) | 设置`dlang`共享库链接器 | | [--dcar](#-dcar) | 设置`dlang`静态库归档器 | | [--rcld](#-rcld) | 设置`rust`链接器 | | [--rcsh](#-rcsh) | 设置`rust`共享库链接器 | | [--rcar](#-rcar) | 设置`rust`静态库归档器 | | [--cu-ccbin](#-cu-ccbin) | 设置`cuda` host编译器 | | [--culd](#-culd) | 设置`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) | 设置静态库归档选项 | ::: tip 注意 如果你想要了解更多参数选项,请运行: `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进行交叉编译的时候,只需要进行如下配置和编译: ```sh $ 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` ```sh $ xmake f -p linux --sdk=/home/toolchains_sdkdir --bin=/usr/opt/bin $ xmake ``` ::: tip 注意 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的版本,则配置如下: ```sh $ xmake f -p linux --sdk=/usr/toolsdk --bin=/opt/bin --cross=armv7-linux- ``` ### --as * 设置`asm`汇编器 如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: ```sh $ xmake f -p linux --sdk=/user/toolsdk --as=armv7-linux-as ``` 如果存在`AS`环境变量的话,会优先使用当前环境变量中指定的值。 ::: tip 注意 如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么编译器工具检测就会失败。 这个时候我们可以通过:`xmake f --as=gcc@/home/xxx/asmips.exe` 设置ccmips.exe编译器作为类gcc的使用方式来编译。 也就是说,在指定编译器为`asmips.exe`的同时,告诉xmake,它跟gcc用法和参数选项基本相同。 ::: ### --cc * 设置c编译器 如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: ```sh $ xmake f -p linux --sdk=/user/toolsdk --cc=armv7-linux-clang ``` 如果存在`CC`环境变量的话,会优先使用当前环境变量中指定的值。 ::: tip 注意 如果指定的编译器名不是那些xmake内置可识别的名字(带有gcc, clang等字样),那么编译器工具检测就会失败。 这个时候我们可以通过:`xmake f --cc=gcc@/home/xxx/ccmips.exe` 设置ccmips.exe编译器作为类gcc的使用方式来编译。 也就是说,在指定编译器为`ccmips.exe`的同时,告诉xmake,它跟gcc用法和参数选项基本相同。 ::: ### --cxx * 设置`c++`编译器 如果还要继续细分选择编译器,则继续追加相关编译器选项,例如: ```sh $ xmake f -p linux --sdk=/user/toolsdk --cxx=armv7-linux-clang++ ``` 如果存在`CXX`环境变量的话,会优先使用当前环境变量中指定的值。 ::: tip 注意 如果指定的编译器名不是那些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`链接器 如果还要继续细分选择链接器,则继续追加相关编译器选项,例如: ```sh $ xmake f -p linux --sdk=/user/toolsdk --ld=armv7-linux-clang++ ``` 如果存在`LD`环境变量的话,会优先使用当前环境变量中指定的值。 ::: tip 注意 如果指定的编译器名不是那些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`共享库链接器 ```sh $ xmake f -p linux --sdk=/user/toolsdk --sh=armv7-linux-clang++ ``` 如果存在`SH`环境变量的话,会优先使用当前环境变量中指定的值。 ::: tip 注意 如果指定的编译器名不是那些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`静态库归档器 ```sh $ xmake f -p linux --sdk=/user/toolsdk --ar=armv7-linux-ar ``` 如果存在`AR`环境变量的话,会优先使用当前环境变量中指定的值。 ::: tip 注意 如果指定的编译器名不是那些xmake内置可识别的名字(带有ar等字样),那么链接器工具检测就会失败。 这个时候我们可以通过:`xmake f --ar=ar@/home/xxx/armips.exe` 设置armips.exe链接器作为类ar的使用方式来编译。 也就是说,在指定链接器为`armips.exe`的同时,告诉xmake,它跟ar用法和参数选项基本相同。 ::: --- --- url: /zh/guide/package-management/repository-management.md --- # 仓库管理命令 {#repository-management} ::: warning 重要提示 **`xmake repo` 仅用于当前工程下的本地包仓库管理**,作用域限制在当前项目内。 如果需要**全局管理仓库**(添加、删除、查看仓库),应该使用 **`xrepo` CLI** 命令,例如: * `xrepo add-repo myrepo https://github.com/mygroup/myrepo` - 全局添加仓库 * `xrepo rm-repo myrepo` - 全局删除仓库 * `xrepo list-repo` - 查看所有全局仓库 详细文档见:[Xrepo 命令使用入门](/zh/guide/package-management/xrepo-cli)。 ::: 我们可以使用 `xmake repo` 来管理**当前工程**的仓库,同时也提供了更为便捷的独立 `xrepo` 包管理器命令,用于**全局**安装、卸载和查找包。 ```sh $ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git ``` 从 v2.2.3 开始,支持添加指定分支的 repo,例如: ```sh $ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git dev ``` ::: tip 注意 我们也可以添加本地仓库路径,即使没有 git 也可以支持,用于在本地快速调试 repo 中的包。 ::: 我们也可以移除已安装的某个仓库: ```sh $ xmake repo --remove myrepo ``` 或者查看所有已添加的仓库: ```sh $ xmake repo --list ``` 如果远程仓库有更新,可以手动执行仓库更新,以获取更多、最新的包: ```sh $ xmake repo -u ``` --- --- url: /zh/posts/try-catch.md --- 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中进行了捕获,并且将错误消息进行输出显示。 这里除了对`pcall/xpcall`进行了封装,用来捕获异常信息,还利用了lua的函数调用语法特性,在只有一个参数传递的情况下,lua可以直接传递一个table类型,并且省略`()` 其实try后面的整个`{...}` 都是一个table而已,作为参数传递给了try函数,其具体实现如下: ```lua function try(block) -- get the try function local try = block[1] assert(try) -- get catch and finally functions local funcs = block[2] if funcs and block[3] then table.join2(funcs, block[2]) end -- try to call it local ok, errors = pcall(try) if not ok then -- run the catch function if funcs and funcs.catch then funcs.catch(errors) end end -- run the finally function if funcs and funcs.finally then funcs.finally(ok, errors) end -- ok? if ok then return errors end end ``` 可以看到这里用了`pcall`来实际调用try块里面的函数,这样就算函数内部出现异常,也不会中断程序,`pcall`会返回false表示运行失败 并且返回实际的出错信息,如果想要自定义格式化错误信息,可以通过调用xpcall来替换pcall,这个接口与pcall相比,多了个错误处理函数: ```lua local ok, errors = xpcall(try, debug.traceback) ``` 你可以直接传入`debug.traceback`,使用默认的traceback处理接口,也可以自定义一个处理函数: ```lua -- traceback function my_traceback(errors) -- make results local level = 2 while true do -- get debug info local info = debug.getinfo(level, "Sln") -- end? if not info or (info.name and info.name == "xpcall") then break end -- function? if info.what == "C" then results = results .. string.format(" [C]: in function '%s'\n", info.name) elseif info.name then results = results .. string.format(" [%s:%d]: in function '%s'\n", info.short_src, info.currentline, info.name) elseif info.what == "main" then results = results .. string.format(" [%s:%d]: in main chunk\n", info.short_src, info.currentline) break else results = results .. string.format(" [%s:%d]:\n", info.short_src, info.currentline) end -- next level = level + 1 end -- ok? return results end -- 调用自定义traceback函数 local ok, errors = xpcall(try, my_traceback) ``` 回到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 } ``` 可以看到,这个基于pcall的`try-catch-finally`异常捕获封装,使用上还是非常灵活的,而且其实现相当的简单 这也充分说明了lua是一门已非常强大灵活,又非常精简的语言。 在[xmake](http://www.xmake.io/cn)的自定义脚本、插件开发中,也是完全基于此异常捕获机制 这样使得扩展脚本的开发非常的精简可读,省去了繁琐的`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默认的处理程序接管,直接中断就行了。。 最后附上`try-catch-finally`实现的相关完整源码: * [try](https://github.com/waruqi/xmake/blob/master/xmake/core/sandbox/modules/try.lua) * [catch](https://github.com/waruqi/xmake/blob/master/xmake/core/sandbox/modules/catch.lua) * [finally](https://github.com/waruqi/xmake/blob/master/xmake/core/sandbox/modules/finally.lua) --- --- url: /zh/posts/project-desciption-examples.md --- ### 描述语法 xmake的描述语法基于lua实现,因此描述语法继承了lua的灵活性和简洁性,并且通过28原则,将描述作用域(简单描述)、脚本作用域(复杂描述)进行分离,使得工程更加的简洁直观,可读性非常好。 因为80%的工程,并不需要很复杂的脚本控制逻辑,只需要简单的几行配置描述,就可满足构建需求,基于这个假设,xmake分离作用域,使得80%的`xmake.lua`文件,只需要这样描述: ```lua target("demo") set_kind("binary") add_files("src/*.c") ``` 而仅有的20%的工程,才需要这样描述: ```lua target("demo") set_kind("shared") set_objectdir("$(buildir)/.objs") set_targetdir("libs/armeabi") add_files("jni/*.c") on_package(function (target) os.run("ant debug") end) on_install(function (target) os.run("adb install -r ./bin/Demo-debug.apk") end) on_run(function (target) os.run("adb shell am start -n com.demo/com.demo.DemoTest") os.run("adb logcat") end) ``` 上面的`function () end`部分属于自定义脚本域,一般情况下是不需要设置的,只有在需要复杂的工程描述、高度定制化需求的情况下,才需要自定义他们,在这个作用域可以使用各种xmake提供的扩展模块,关于这个的更多介绍,见:[xmake 描述语法和作用域详解](https://xmake.io/zh/)。 而上面的代码,也是一个自定义混合构建jni和java代码的android工程,可以直接通过`xmake run`命令,实现一键自动构建、安装、运行apk程序。 下面介绍一些比较常用的xmake描述实例: ### 构建一个可执行程序 ```lua target("demo") set_kind("binary") add_files("src/*.c") ``` 这是一个最简单经典的实例,一般情况下,这种情况,你不需要自己写任何`xmake.lua`文件,在当前代码目录下,直接执行`xmake`命令,就可以完成构建,并且会自动帮你生成一个`xmake.lua`。 关于自动生成的详细信息,见:[xmake智能代码扫描编译模式,无需手写任何make文件](https://xmake.io/zh/)。 ### 构建一个可配置切换的库程序 ```lua target("demo") set_kind("$(kind)") add_files("src/*.c") ``` 可通过配置,切换是否编译动态库还是静态库: ```bash $ xmake f --kind=static; xmake $ xmake f --kind=shared; xmake ``` ### 增加debug和release编译模式支持 也许默认的几行描述配置,已经不能满足你的需求,你需要可以通过切换编译模式,构建debug和release版本的程序,那么只需要: ```lua if is_mode("debug") then set_symbols("debug") set_optimize("none") end if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end target("demo") set_kind("binary") add_files("src/*.c") ``` 你只需要通过配置来切换构建模式: ```bash $ xmake f -m debug; xmake $ xmake f -m release; xmake ``` `[-m|--mode]`属于内置选项,不需要自己定义`option`,就可使用,并且模式的值是用户自己定义和维护的,你可以在`is_mode("xxx")`判断各种模式状态。 ### 通过自定义脚本签名ios程序 ios的可执行程序,在设备上运行,需要在构建完成后进行签名,这个时候就可以使用自定义脚本来实现: ```lua target("demo") set_kind("binary") add_files("src/*.m") after_build(function (target)) os.run("ldid -S %s", target:targetfile()) end ``` 这里只是用ldid程序做了个假签名,只能在越狱设备上用哦,仅仅作为例子参考哈。 ### 内置变量和外置变量 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) ``` 当然这种变量模式,也是可以扩展的,默认通过`xmake f --var=val`命令,配置的参数都是可以直接获取,例如: ```lua target("test") add_defines("-DTEST=$(var)") ``` 既然支持直接从配置选项中获取,那么当然也就能很方便的扩展自定义的选项,来获取自定义的变量了,具体如何自定义选项见:[option](https://xmake.io/zh/) ### 修改目标文件名 我们可以通过内建变量,将生成的目标文件按不同架构和平台进行分离,例如: ```lua target("demo") set_kind("binary") set_basename("demo_$(arch)") set_targetdir("$(buildir)/$(plat)") ``` 之前的默认设置,目标文件会生成为`build\demo`,而通过上述代码的设置,目标文件在不同配置构建下,路径和文件名也不尽相同,执行: ```bash $ xmake f -p iphoneos -a arm64; xmake ``` 则目标文件为:`build/iphoneos/demo_arm64`。 ### 添加子目录工程模块 如果你有多个target子模块,那么可以在一个`xmake.lua`中进行定义,例如: ```lua target("demo") set_kind("binary") add_files("src/demo.c") target("test") set_kind("binary") add_files("src/test.c") ``` 但是,如果子模块非常多,那么放置在一个xmake文件,就显得有些臃肿了,可以放置到独立模块的子目录去: ```lua target("demo") set_kind("binary") add_files("src/demo.c") add_subdirs("src/test") ``` 通过上述代码,关联一个子工程目录,在里面加上`test`的工程目标就行了。 ### 安装头文件 ``` target("tbox") set_kind("static") add_files("src/*.c") add_headers("../(tbox/**.h)|**/impl/**.h") set_headerdir("$(buildir)/inc") ``` 安装好的头文件位置和目录结构为:`build/inc/tbox/*.h`。 其中`../(tbox/**.h)`带括号的部分,为实际要安装的根路径,`|**/impl/**.h`部分用于排除不需要安装的文件。 其通配符匹配规则、排除规则可参考[add\_files](https://xmake.io/zh/)。 ### 多目标依赖构建 多个target工程目标,默认构建顺序是未定义的,一般按顺序的方式进行,如果你需要调整构建顺序,可以通过添加依赖顺序来实现: ```lua target("test1") set_kind("static") set_files("*.c") target("test2") set_kind("static") set_files("*.c") target("demo") add_deps("test1", "test2") add_links("test1", "test2") ``` 上面的例子,在编译目标demo的时候,需要先编译test1, test2目标,因为demo会去用到它们。 ### 合并静态库 xmake的[add\_files](https://xmake.io/zh/)接口功能是非常强大的,不仅可以支持多种语言文件的混合添加构建,还可以直接添加静态库,进行自动合并库到当前的工程目标中去。 我们可以这么写: ```lua target("demo") set_kind("static") add_files("src/*.c", "libxxx.a", "lib*.a", "xxx.lib") ``` 直接在编译静态库的时候,合并多个已有的静态库,注意不是链接哦,这跟[add\_links](https://xmake.io/zh/)是有区别的。 并且你也可以直接追加对象文件: ```lua target("demo") set_kind("binary") add_files("src/*.c", "objs/*.o") ``` ### 添加自定义配置选项 我们可以自己定义一个配置选项,例如用于启用test: ```lua option("test") set_default(false) set_showmenu(true) add_defines("-DTEST") ``` 然后关联到指定的target中去: ```lua target("demo") add_options("test") ``` 这样,一个选项就算定义好了,如果这个选项被启用,那么编译这个target的时候,就会自动加上`-DTEST`的宏定义。 上面的设置,默认是禁用`test`选项的,接下来我们通过配置去启用这个选项: ```bash $ xmake f --test=y $ xmake ``` xmake的选项支持是非常强大的,除了上述基础用法外,还可以配置各种检测条件,实现自动检测,具体详情可参考:[option](https://xmake.io/zh/)和[依赖包的添加和自动检测机制](https://xmake.io/zh/)。 ### 添加第三方依赖包 在target作用域中,添加集成第三方包依赖,例如: ```lua target("test") set_kind("binary") add_packages("zlib", "polarssl", "pcre", "mysql") ``` 这样,在编译test目标时,如果这个包存在的,将会自动追加包里面的宏定义、头文件搜索路径、链接库目录,也会自动链接包中所有库。 用户不再需要自己单独调用`add_links`,`add_includedirs`, `add_ldflags`等接口,来配置依赖库链接了。 对于如何设置包搜索目录,可参考[add\_packagedirs](https://xmake.io/zh/)接口,依赖包详情请参考:[依赖包的添加和自动检测机制](https://xmake.io/zh/)。 ### 生成配置头文件 如果你想在xmake配置项目成功后,或者自动检测某个选项通过后,把检测的结果写入配置头文件,那么需要调用这个接口来启用自动生成`config.h`文件。 使用方式例如: ```lua target("test") set_config_h("$(buildir)/config.h") set_config_h_prefix("TB_CONFIG") ``` 当这个target中通过下面的这些接口,对这个target添加了相关的选项依赖、包依赖、接口依赖后,如果某依赖被启用,那么对应的一些宏定义配置,会自动写入被设置的`config.h`文件中去。 * [add\_options](https://xmake.io/zh/) * [add\_packages](https://xmake.io/zh/) * [add\_cfuncs](https://xmake.io/zh/) * [add\_cxxfuncs](https://xmake.io/zh/) 这些接口,其实底层都用到了option选项中的一些检测设置,例如: ```lua option("wchar") -- 添加对wchar_t类型的检测 add_ctypes("wchar_t") -- 如果检测通过,自动生成TB_CONFIG_TYPE_HAVE_WCHAR的宏开关到config.h add_defines_h_if_ok("$(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") ``` ### 检测库头文件和接口 我们可以在刚刚生成的`config.h`中增加一些库接口检测,例如: ```lua target("demo") -- 设置和启用config.h set_config_h("$(buildir)/config.h") set_config_h_prefix("TEST") -- 仅通过参数一设置模块名前缀 add_cfunc("libc", nil, nil, {"sys/select.h"}, "select") -- 通过参数三,设置同时检测链接库:libpthread.a add_cfunc("pthread", nil, "pthread", "pthread.h", "pthread_create") -- 通过参数二设置接口别名 add_cfunc(nil, "PTHREAD", nil, "pthread.h", "pthread_create") ``` 生成的`config.h`结果如下: ```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 ``` 这样我们在代码里面就可以根据接口的支持力度来控制代码编译了。 ### 自定义插件任务 task域用于描述一个自定义的任务实现,与[target](https://xmake.io/zh/)和[option](https://xmake.io/zh/)同级。 例如,这里定义一个最简单的任务: ```lua task("hello") -- 设置运行脚本 on_run(function () print("hello xmake!") end) ``` 这个任务只需要打印`hello xmake!`,那如何来运行呢? 由于这里没有使用[set\_menu](https://xmake.io/zh/)设置菜单,因此这个任务只能在`xmake.lua`的自定义脚本或者其他任务内部调用,例如: ```lua target("test") after_build(function (target) -- 导入task模块 import("core.project.task") -- 运行hello任务 task.run("hello") end) ``` 此处在构建完test目标后运行hello任务,当然我们还可以传递参数哦: ```lua task("hello") on_run(function (arg1, arg2, arg3) print("hello xmake!", arg1, arg2, arg3) end) target("test") after_build(function (target) import("core.project.task") task.run("hello", {}, "arg1", "arg2", "arg3") end) ``` 上述`task.run`的`{}`这个是用于传递插件菜单中的参数,这里没有通过[set\_menu](https://xmake.io/zh/)设置菜单,此处传空。 xmake的插件支持也是功能很强大的,并且提供了很多内置的使用插件,具体请参考:[xmake插件手册](https://xmake.io/zh/)和[task手册](https://xmake.io/zh/) 或者可以参考xmake自带的一些[插件demo](https://github.com/xmake-io/xmake/blob/master/xmake/plugins/echo/xmake.lua)。 ### 另外一种语法风格 xmake除了支持最常使用的`set-add`描述风格外,还支持另外一种语法风格:`key-val`,例如: ```lua target { name = "test", defines = "DEBUG", files = {"src/*.c", "test/*.cpp"} } ``` 这个等价于: ```lua target("test") set_kind("static") add_defines("DEBUG") add_files("src/*.c", "test/*.cpp") ``` 用户可以根据自己的喜好来选择合适的风格描述,但是这边的建议是: ``` * 针对简单的工程,不需要太过复杂的条件编译,可以使用key-val方式,更加精简,可读性好 * 针对复杂工程,需要更高的可控性,和灵活性的话,建议使用set-add方式 * 尽量不要两种风格混着写,虽然是支持的,但是这样对整个工程描述会感觉很乱,因此尽量统一风格作为自己的描述规范 ``` 另外,不仅对target,像option, task, template都是支持两种方式设置的,例如: ```lua -- set-add风格 option("demo") set_default(true) set_showmenu(true) set_category("option") set_description("Enable or disable the demo module", " =y|n") ``` ```lua -- key-val风格 option { name = "demo", default = true, showmenu = true, category = "option", desciption = {"Enable or disable the demo module", " =y|n"} } ``` 自定义的任务或者插件可以这么写: ```lua -- set-add风格 task("hello") on_run(function () print("hello xmake!") end) set_menu { usage = "xmake hello [options]", description = "Hello xmake!", options = {} } ``` ```lua -- key-val风格 task { name = "hello", run = (function () print("hello xmake!") end), menu = { usage = "xmake hello [options]", description = "Hello xmake!", options = {} } } ``` ### 结语 更多描述说明,可直接阅读[xmake的官方手册](https://xmake.io/zh/),上面提供了完整的api文档和使用描述。 --- --- url: /zh/posts/compiler-features.md --- 如果我们要写跨平台的c/c++代码,很多时候需要处理由于不同编译器对c/c++各个标准支持力度不同导致的兼容性问题,一般通常的解决办法是:自己在代码中通过宏去判断各个编译器的版本、内置宏、标准库宏、`__has_feature`等来检测处理。 自己如果在代码中按上述的方式检测,会很繁琐,尤其是像c++这种存在大量语法特性,如果一一检测过来,工作量是非常大的。 #### 通过构建工具预先检测编译特性 另外比较省事的方式,就是依赖构建工具提前做好检测,然后把检测结果作为宏添加到编译中去,这样代码只需要判断对应的特性宏是否存在,就可以进行处理了。 在cmake中就有类似的检测机制,非常强大,因此xmake也对其进行了支持,提供更加灵活强大的编译器特性预先检测支持: ```lua target("test") on_load(function (target) import("core.tool.compiler") if compiler.has_features("cxx_constexpr") then target:add("defines", "HAS_CXX_CONSTEXPR=1") end end) ``` 通过`core.tool.compiler`模块的[compiler.has\_features](https://xmake.io/zh/)接口,在`xmake.lua`中预先判断当前编译期支持的语言特性,实现条件编译。 此处也是参考了cmake的设计,具体详情见:[issues#83](https://github.com/xmake-io/xmake/issues/83)。 上述代码,在加载target的时候,判断当前编译器是否支持c++的常量表达式语法特性,如果支持则添加宏定义:`HAS_CXX_CONSTEXPR=1`。 我们也可以在判断时候,追加一些参数控制编译选项,例如上述特性需要`c++11`支持,我们可以启用它: ```lua if compiler.has_features({"c_static_assert", "cxx_constexpr"}, {languages = "cxx11"}) then -- ok end ``` 通过上面的代码可以看到,此接口是可以同时检测多个特性的,返回值为实际支持的特性列表。 如果之前对这个target已经设置了`c++11`,那么我们也可以传入target对象,继承target的所有设置,甚至指定一些其他扩展编译配置: ```lua if compiler.has_features("cxx_constexpr", {target = target, defines = "..", includedirs = ".."}) then -- ok end ``` #### 批量编译器特性检测 c++的语言特性非常多,这个时候我们可以通过脚本实现快速的批量检测: ```lua target("test") on_load(function (target) import("core.tool.compiler") for feature, _ in pairs(compiler.features("cxx", {target = target})) do -- 传入target在检测特性时继承target的所有编译配置 target:add("defines", "has_feature_" .. feature) end end) ``` 上述代码,会在加载target的时候,把当前编译器对c++的所有支持特性,都添加到target的宏定义中进行编译,例如:`-Dhas_feature_cxx_constexpr`。 我们只需要在代码中,通过判断对应的特性宏是否存在就行了: ```c #ifdef has_feature_cxx_constexpr // TODO #endif ``` 目前支持的所有c/c++编译器特性列表,见:[compiler.features](https://xmake.io/zh/) #### 更加底层的检测接口 如果我们要指定获取具体哪个编译器的特性支持,则需要更加底层的接口支持了,例如: ```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"}) ``` [lib.detect.has\_features](https://xmake.io/zh/)属于探测模块的接口,可以指定需要检测的工具名,例如这里通过传入clang,只对clang编译器进行检测。 当然此接口,还可以检测其他非编译器的工具特性,更加的通用。 #### 通过自定义c/c++代码片段来检测特性 对于一些复杂的编译器特性,连[compiler.has\_features](https://xmake.io/zh/)都无法检测到的时候,可以通过自定义代码片段尝试编译来检测它。 ```lua import("lib.detect.check_cxsnippets") local ok = check_cxsnippets("constexpr int f(int x) { return x ? x+f(x-1) : 0; } constexpr int x = f(5); static_assert(x == 15);", {sourcekind = "cxx", languages = "cxx11"}) ``` 上述代码通过自定义一个constexpr的测试代码,去检测c++11的constexpr支持。 此接口是[detect.has\_cfuncs](https://xmake.io/zh/), [detect.has\_cincludes](https://xmake.io/zh/)和[detect.has\_ctypes](https://xmake.io/zh/)等接口的通用版本,也更加底层。 因此我们可以用它来检测:types, functions, includes 还有 links,或者是组合起来一起检测。 第一个参数为代码片段列表,一般用于一些自定义特性的检测,如果为空,则可以仅仅检测可选参数中条件,例如: ```lua local ok = check_cxsnippets({"void test() {}", "void test2() {}"}, {types = {"wchar_t", "char*"}, includes = "stdio.h", funcs = {"sigsetjmp", "sigsetjmp((void*)0, 0)"}}) ``` 上面那个调用,会去同时检测types, includes和funcs是否都满足,如果通过返回true。 --- --- url: /zh/posts/compile-swift.md --- xmake不仅可以支持 c/c++文件,同时也支持 objc/c++,甚至swift代码的编译。 我们先看一下如何创建Swift工程,首先执行--help,看下帮助文档: ```bash xmake create --help ``` 显示如下: ``` Usage: xmake create [options] [target] Create a new project. Options: -n NAME, --name=NAME The project name. -f FILE, --file=FILE Create a given xmake.lua file. (default: xmake.lua) -P PROJECT, --project=PROJECT Create from the given project directory. Search priority: 1. The Given Command Argument 2. The Envirnoment Variable: XMAKE_PROJECT_DIR 3. The Current Directory -l LANGUAGE, --language=LANGUAGE The project language (default: c) - c - c++ - objc - objc++ - swift -t TEMPLATE, --template=TEMPLATE Select the project template id of the given language. (default: 1) - language: c 1. The Console Program 2. The Console Program (tbox) 3. The Shared Library 4. The Shared Library (tbox) 5. The Static Library 6. The Static Library (tbox) - language: c++ 1. The Console Program 2. The Console Program (tbox) 3. The Shared Library 4. The Shared Library (tbox) 5. The Static Library 6. The Static Library (tbox) - language: objc 1. The Console Program - language: objc++ 1. The Console Program - language: swift 1. The Console Program -v, --verbose Print lots of verbose information. --version Print the version number and exit. -h, --help Print this help message and exit. target Create the given target. Uses the project name as target if not exists. ``` 可以看到 只要指定 语言为swift,工程模板选择1,就能创建一个基于swift的控制台项目,具体操作如下: ```bash xmake create -l swift -t 1 -P /tmp/test -n swift_test ``` 执行完成后,就会在/tmp/test目录下自动生成一个名为swift\_test的工程 我们看下生成好的xmake.lua ```lua -- the debug mode if modes("debug") then -- enable the debug symbols set_symbols("debug") -- disable optimization set_optimize("none") end -- the release mode if modes("release") then -- set the symbols visibility: hidden set_symbols("hidden") -- enable fastest optimization set_optimize("fastest") -- strip all symbols set_strip("all") end -- add target add_target("swift_test") -- set kind set_kind("binary") -- add files add_files("src/*.swift") ``` 可以看到,和平常的xmake.lua描述没什么区别,唯一的改动就是:`add_files("src/*.swift") ` 而生成的main.swift代码,也很简单: ```swift import Foundation print("hello world!") ``` 现在我们进入/tmp/test目录编译下: ```bash cd /tmp/test xmake ``` 编译完后,就可以运行了: ``` xmake r swift_test ``` 显示效果: ``` hello world! ``` 搞定。 --- --- url: /zh/posts/project-compile.md --- 如果你只想编译当前主机环境的平台,例如在windows上编译windows版本,在macosx上编译macosx版本,那么你只需要敲以下命令即可: ```bash xmake ``` 因为xmake默认会去检测当前的环境,默认编译当前主机的平台版本,不需要做额外的配置,并且默认编译的是release版本。 如果工程里面有多个目标,那么上面的命令,会去编译所有目标,如果只想编译指定一个目标,例如:test,那么只需执行: ```bash xmake test ``` 如果你想编译debug版本,那么需要做些简单的配置: ```bash xmake config --mode=debug xmake ``` xmake针对每个命令和参数,都提供了简写版本: ```bash xmake f -m debug xmake ``` 注:为了提高灵活性,release版本和debug版本的编译选项设置,需要自己在工程描述文件中描述,如果没有设置的话,release和debug版本生成的程序是一样的。 如果你想强制重新构建所有,可以执行: ```bash xmake -r xmake --rebuild ``` 如果要指定编译具体某个架构,可以这么进行编译: ```bash xmake f -a armv7 xmake ``` 一般情况下,如果没有指定架构,默认会去使用指定平台的默认架构,例如:macosx下默认是x86\_64,iphoneos下士armv7 如果想要指定编译其他平台,例如在macosx上编译iphoneos的版本,那么: ```bash xmake f -p iphoneos xmake ``` 编译android版本: ```bash xmake f -p android --ndk=xxxx xmake ``` 虽然配置完后,每次编译不需要重新配置,但是如果切换编译目标平台到ios、linux,那么之前ndk的设置就被清除了,下次得重新配置。 如果想要更加方便的不同平台间来回切换编译,可以将ndk设置到全局配置中,例如: ```bash -- 将ndk设置到全局配置中 xmake g --ndk=xxx -- 切换到android编译平台,不需要每次都设置ndk了 xmake f -p android xmake -r -- 切换到ios编译平台 xmake f -p iphoneos xmake -r ``` 编译windows版本,很简单,只要你机子装了vs,xmake会去自动检测,不需要做额外的配置,只需要打开cmd,进入你的工程目录, 然后执行xmake就行了。 使用其他交叉工具链进行编译: ```bash xmake f -p android -a armv7-a --cross=arm-linux-androideabi- --toolchains=/xxxx/bin xmake ``` 默认在编译配置的时候,会去缓存上一次的配置,这样每次配置只需要修改部分参数就行了,不需要每次全部重新配置 如果你想重新配置所有,清楚原有的缓存,可以加上--clean参数: ```bash xmake f -c xmake f --clean xmake ``` xmake在配置的时候,会去检测工程依赖的一些接口和链接库,如果你想要看具体详细的配置检测信息,可以加上--verbose参数,回显配置信息 ```bash xmake f -c -v xmake f --clean --verbose xmake ``` xmake还支持在编译的时候,手动设置一些编译选项和工具,你可以执行`xmake f --help`看下: ```bash Usage: xmake config|f [options] [target] Configure the project. Options: -c, --clean Clean the cached configure and configure all again. -p PLAT, --plat=PLAT Compile for the given platform. (default: macosx) - android - iphoneos - iphonesimulator - linux - macosx - mingw - watchos - watchsimulator - windows -a ARCH, --arch=ARCH Compile for the given architecture. (default: auto) - android: armv5te armv6 armv7-a armv8-a arm64-v8a - iphoneos: armv7 armv7s arm64 - iphonesimulator: i386 x86_64 - linux: i386 x86_64 - macosx: i386 x86_64 - mingw: i386 x86_64 - watchos: armv7 armv7s arm64 - watchsimulator: i386 x86_64 - windows: x86 x64 amd64 x86_amd64 -m MODE, --mode=MODE Compile for the given mode. (default: release) - debug - release - profile -k KIND, --kind=KIND Compile for the given target kind. (default: static) - static - shared - binary --host=HOST The current host environment. (default: macosx) --make=MAKE Set the make path. (default: auto) --ccache=CCACHE Enable or disable the c/c++ compiler cache. (default: auto) --cross=CROSS The cross toolchains prefix .e.g - i386-mingw32- - arm-linux-androideabi- --toolchains=TOOLCHAINS The cross toolchains directory --cc=CC The C Compiler --cxx=CXX The C++ Compiler --cflags=CFLAGS The C Compiler Flags --cxflags=CXFLAGS The C/C++ compiler Flags --cxxflags=CXXFLAGS The C++ Compiler Flags --as=AS The Assembler --asflags=ASFLAGS The Assembler Flags --sc=SC The Swift Compiler --scflags=SCFLAGS The Swift Compiler Flags --ld=LD The Linker --ldflags=LDFLAGS The Binary Linker Flags --ar=AR The Static Library Linker --arflags=ARFLAGS The Static Library Linker Flags --sh=SH The Shared Library Linker --shflags=SHFLAGS The Shared Library Linker Flags --ndk=NDK The NDK Directory --ndk_sdkver=NDK_SDKVER The SDK Version for NDK (default: auto) --mm=MM The Objc Compiler --mxx=MXX The Objc++ Compiler --mflags=MFLAGS The Objc Compiler Flags --mxflags=MXFLAGS The Objc/c++ Compiler Flags --mxxflags=MXXFLAGS The Objc++ Compiler Flags --xcode_dir=XCODE_DIR The Xcode Application Directory (default: auto) --xcode_sdkver=XCODE_SDKVER The SDK Version for Xcode (default: auto) --target_minver=TARGET_MINVER The Target Minimal Version (default: auto) --mobileprovision=MOBILEPROVISION The Provisioning Profile File (default: auto) --codesign=CODESIGN The Code Signing Indentity (default: auto) --entitlements=ENTITLEMENTS The Code Signing Entitlements (default: auto) --vs=VS The Microsoft Visual Studio (default: auto) -f FILE, --file=FILE Read a given xmake.lua file. (default: xmake.lua) -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 -o BUILDIR, --buildir=BUILDIR Set the build directory. (default: build) -v, --verbose Print lots of verbose information. --version Print the version number and exit. -h, --help Print this help message and exit. target Configure for the given target. (default: all) ``` --- --- url: /zh/guide/package-management/using-official-packages.md --- # 使用官方包 {#using-official-packages} 该功能在 2.2.2 版本后已初步支持,用法上更加简单,只需设置对应的依赖包即可,例如: ```lua add_requires("tbox 1.6.*", "libpng ~1.16", "zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("tbox", "libpng", "zlib") ``` 上面的 `add_requires` 用于描述当前项目需要的依赖包,而 `add_packages` 用于将依赖包应用到 test 目标,只有设置这个才会自动追加 links、linkdirs、includedirs 等设置。 然后直接执行编译即可: ```sh $ xmake ``` xmake 会去远程拉取相关源码包,然后自动编译安装,最后编译项目,进行依赖包的链接,具体效果见下图: 关于包依赖管理的更多相关信息和进展见相关 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 库的空工程: ```sh $ xmake create -t console_tbox test $ cd test ``` 执行编译即可,如果当前没有安装 tbox 库,则会自动下载安装后使用: ```sh $ xmake ``` 切换到 iphoneos 平台进行编译,将会重新安装 iphoneos 版本的 tbox 库进行链接使用: ```sh $ xmake f -p iphoneos $ xmake ``` 切换到 android 平台 arm64-v8a 架构编译: ```sh $ xmake f -p android [--ndk=~/android-ndk-r16b] $ xmake ``` ## 语义版本设置 xmake 的依赖包管理完全支持语义版本选择,例如:"~1.6.1",关于语义版本的具体描述见: 一些语义版本写法: ```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 包启用此选项。 我们可以通过在工程目录中执行:`xmake require --info tbox` 来获取指定包所有可配置参数列表和取值说明。 例如: ```sh xmake require --info spdlog require(spdlog): -> requires: -> plat: macosx -> arch: x86_64 -> configs: -> header_only: true -> shared: false -> vs_runtime: MT -> debug: false -> fmt_external: true -> noexcept: false -> configs: -> header_only: Use header only (default: true) -> fmt_external: Use external fmt library instead of bundled (default: false) -> noexcept: Compile with -fno-exceptions. Call abort() on any spdlog exceptions (default: false) -> configs (builtin): -> debug: Enable debug symbols. (default: false) -> shared: Enable shared library. (default: false) -> cflags: Set the C compiler flags. -> cxflags: Set the C/C++ compiler flags. -> cxxflags: Set the C++ compiler flags. -> asflags: Set the assembler flags. -> vs_runtime: Set vs compiler runtime. (default: MT) -> values: {"MT","MD"} ``` 其中,configs 里是 spdlog 包自身提供的可配置参数,而下方带有 builtin 的 configs 部分,是所有包都会有的内置配置参数。 最上面 requires 部分,是项目当前配置值。 ::: tip 注意 `vs_runtime` 是用 emsvc 下 vs runtime 的设置,v2.2.9 版本中,还支持所有 static 依赖包的自动继承,也就是说 spdlog 如果设置了 MD,那么它依赖的 fmt 包也会自动继承设置 MD。 ::: 可以看到,我们已经能够很方便地定制化获取需要的包,但每个包自身也许有很多依赖,如果这些依赖也要各种定制化配置,怎么办? 可以通过 `add_requireconfs` 去重写内部依赖包的配置参数。 ### 安装任意版本的包 默认情况下,`add_requires("zlib >1.2.x")` 只能选择到 `xmake-repo` 仓库中存在的包版本,因为每个版本的包都会有一个 sha256 校验值,用于包的完整性校验。 因此,未知版本的包不存在校验值,xmake 默认不允许选择使用,这样更安全。 那么,如果我们需要的包版本无法选择使用怎么办呢?有两种方式,一种是提交一个 PR 给 [xmake-repo](https://github.com/xmake-io/xmake-repo),增加指定包的新版本以及对应的 sha256,例如: ```lua package("zlib") add_versions("1.2.10", "8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017") add_versions("1.2.11", "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1") ``` 另外,还有一种方式,就是用户传递 `{verify = false}` 配置给 `add_requires`,强制忽略包的文件完整性校验,这样就可以安装任意版本的包。 当然,这也会存在一定的安全性以及包不完整的风险,这就需要用户自己去选择评估了。 ```lua add_requires("zlib 1.2.11", {verify = false}) ``` ### 禁用外部头文件搜索路径 默认情况下,通过 `add_requires` 安装的包会采用 `-isystem` 或者 `/external:I` 来引用里面的头文件路径,这通常能够避免一些包头文件引入的不可修改的警告信息, 但是,我们还是可以通过设置 `{external = false}` 来禁用外部头文件,切回 `-I` 的使用。 默认启用了 external 外部头文件的编译 flags 如下: ```sh -isystem /Users/ruki/.xmake/packages/z/zlib/1.2.11/d639b7d6e3244216b403b39df5101abf/include ``` 手动关闭 external 外部头文件的编译 flags 如下: ```lua add_requires("zlib 1.x", {external = false}) ``` ```sh -I /Users/ruki/.xmake/packages/z/zlib/1.2.11/d639b7d6e3244216b403b39df5101abf/include ``` ## 使用自建私有包仓库 {#using-self-built-private-package-repository} 如果需要的包不在官方仓库[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` 我们可以通过下面的命令进行仓库添加: ```sh $ xmake repo --add myrepo git@github.com:myrepo/xmake-repo.git [branch] ``` ::: tip 注意 `[branch]` 是可选的,我们也可以切换到指定repo分支 ::: 或者我们直接写在xmake.lua中: ```lua add_repositories("my-repo git@github.com:myrepo/xmake-repo.git") ``` 同样,我们也可以切换到指定repo分支 ```lua add_repositories("my-repo git@github.com:myrepo/xmake-repo.git dev") ``` 如果我们只是想添加一两个私有包,这个时候特定去建立一个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/xmake-io/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") ``` 关于如何编写自定义包描述规则,详情见:[添加包到仓库](/zh/guide/package-management/package-distribution.html#submit-packages-to-the-official-repository) ## 依赖包的锁定和升级 {#lock-and-upgrade-package} v2.5.7 之后,新增依赖包的版本锁定,类似 npm 的 package.lock, cargo 的 cargo.lock。 比如,我们引用一些包,默认情况下,如果不指定版本,那么 xmake 每次都会自动拉取最新版本的包来集成使用,例如: ```lua add_requires("zlib") ``` 但如果上游的包仓库更新改动,比如 zlib 新增了一个 1.2.11 版本,或者安装脚本有了变动,都会导致用户的依赖包发生改变。 这容易导致原本编译通过的一些项目,由于依赖包的变动出现一些不稳定因素,有可能编译失败等等。 为了确保用户的项目每次使用的包都是固定的,我们可以通过下面的配置去启用包依赖锁定。 ```lua set_policy("package.requires_lock", true) ``` 这是一个全局设置,必须设置到全局根作用域,如果启用后,xmake 执行完包拉取,就会自动生成一个 `xmake-requires.lock` 的配置文件。 它包含了项目依赖的所有包,以及当前包的版本等信息。 ```lua { __meta__ = { version = "1.0" }, ["macosx|x86_64"] = { ["cmake#31fecfc4"] = { repo = { branch = "master", commit = "4498f11267de5112199152ab030ed139c985ad5a", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.21.0" }, ["glfw#31fecfc4"] = { repo = { branch = "master", commit = "eda7adee81bac151f87c507030cc0dd8ab299462", url = "https://github.com/xmake-io/xmake-repo.git" }, version = "3.3.4" }, ["opengl#31fecfc4"] = { repo = { branch = "master", commit = "94d2eee1f466092e04c5cf1e4ecc8c8883c1d0eb", url = "https://github.com/xmake-io/xmake-repo.git" } } } } ``` 当然,我们也可以执行下面的命令,强制升级包到最新版本。 ```sh $ xmake require --upgrade upgrading packages .. zlib: 1.2.10 -> 1.2.11 1 package is upgraded! ``` --- --- url: /zh/guide/package-management/using-local-packages.md --- # 使用本地包 {#using-local-packages} ## 生成本地包 {#local-package-generate} 2.5.5 版本之后,我们提供了一种新的本地包打包方案,将会更加无缝地对接 `add_requires` 和 `add_packages`。 我们执行 `xmake package` 命令就能够生成默认的新版打包格式。 ```sh $ xmake package package(foo): build/packages/f/foo generated ``` 它会生成 `build/packages/f/foo/xmake.lua` 文件,内容如下: ```lua package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") on_load(function (package) package:set("installdir", path.join(os.scriptdir(), package:plat(), package:arch(), package:mode())) end) on_fetch(function (package) local result = {} result.links = "foo" result.linkdirs = package:installdir("lib") result.includedirs = package:installdir("include") return result end) ``` 其实就是采用 `package()` 来定义描述本地包,和远程包一样。 而生成的目录结构如下: ```sh $ tree build/packages/f/foo/ build/packages/f/foo/ ├── macosx │   └── x86_64 │   └── release │   ├── include │   │   └── foo.h │   └── lib │   └── libfoo.a └── xmake.lua ``` ## 使用本地包 {#local-package-use} 我们也能够使用 `add_requires`/`add_repositories` 接口来无缝集成这个包。 ```lua add_rules("mode.debug", "mode.release") add_repositories("local-repo build") add_requires("foo") target("bar") set_kind("binary") add_files("src/*.cpp") add_packages("foo") ``` 其中,add\_repositories 配置指定本地包的仓库根目录,然后就可以通过 `add_requires` 来引用这个包了。 另外,生成的本地包还有一个特性,就是支持 `target/add_deps`,会自动关联多个包的依赖关系,集成时也会自动对接所有依赖链。 这里有完整的[测试例子](https://github.com/xmake-io/xmake/blob/dev/tests/actions/package/localpkg/test.lua)。 ```sh "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/bar build/.objs/bar/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version-min=10.15 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk -stdlib=libc++ -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/f/foo/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/s/sub/macosx/x86_64/release/lib -L/Users/ruki/projects/personal/xmake/tests/actions/package/localpkg/bar/build/packages/a/add/macosx/x86_64/release/lib -Wl,-x -lfoo -lsub -ladd -lz ``` ## 使用通过 CMake 查找到的包 {#local-package-cmake} 现在 cmake 已经是事实上的标准,所以 CMake 提供的 find\_package 已经可以查找大量的库和模块,我们完全复用 cmake 的这部分生态来扩充 xmake 对包的集成。 我们可以通过 `find_package("cmake::xxx")` 去借助 cmake 来找一些包,xmake 会自动生成一个 cmake 脚本来调用 cmake 的 find\_package 去查找一些包,获取里面包信息。 例如: ```sh $ xmake l find_package cmake::ZLIB { links = { "z" }, includedirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/include" }, linkdirs = { "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10. 15.sdk/usr/lib" } } $ xmake l find_package cmake::LibXml2 { links = { "xml2" }, includedirs = { "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/libxml2" }, linkdirs = { "/usr/lib" } } ``` ### 在项目中集成包 {#local-package-cmake-integrate} 如果我们在 xmake.lua 项目配置中集成查找 cmake 的依赖包,通常不需要直接使用 find\_package,可以用更加通用、简单的包集成方式。 ```lua add_requires("cmake::ZLIB", {alias = "zlib", system = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` 我们指定 `system = true` 告诉 xmake 强制从系统中调用 cmake 查找包,如果找不到,不再走安装逻辑,因为 cmake 没有提供类似 vcpkg/conan 等包管理器的安装功能,只提供了包查找特性。 ### 指定版本 {#local-package-cmake-version} ```lua add_requires("cmake::OpenCV 4.1.1", {system = true}) ``` ### 指定组件 {#local-package-cmake-components} ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}})} ``` ### 预设开关 {#local-package-cmake-presets} ```lua add_requires("cmake::Boost", {system = true, configs = {components = {"regex", "system"}, presets = {Boost_USE_STATIC_LIB = true}}}) ``` 相当于内部调用 find\_package 查找包之前,在 CMakeLists.txt 中预定义一些配置,控制 find\_package 的查找策略和状态。 ```cmake set(Boost_USE_STATIC_LIB ON) -- will be used in FindBoost.cmake find_package(Boost REQUIRED COMPONENTS regex system) ``` ### 设置环境变量 {#local-package-cmake-envs} ```lua add_requires("cmake::OpenCV", {system = true, configs = {envs = {CMAKE_PREFIX_PATH = "xxx"}}}) ``` ### 指定自定义 FindFoo.cmake 模块脚本目录 {#local-package-cmake-moduledirs} mydir/cmake\_modules/FindFoo.cmake ```lua add_requires("cmake::Foo", {system = true, configs = {moduledirs = "mydir/cmake_modules"}}) ``` 相关 issues: [#1632](https://github.com/xmake-io/xmake/issues/1632) ### 指定链接项 {#local-package-cmake-link-libraries} 对于 cmake 包,我们新增了 `link_libraries` 配置选项,让用户在查找使用 cmake 包的时候,可以自定义配置包依赖的链接库,甚至对 target 链接的支持。 ```lua add_requires("cmake::xxx", {configs = {link_libraries = {"abc::lib1", "abc::lib2"}}}) ``` xmake 在查找 cmake 包的时候,会自动追加下面的配置,改进对 links 库的提取。 ```cmake target_link_libraries(test PRIVATE ABC::lib1 ABC::lib2) ``` ### 指定搜索模式 {#local-package-cmake-search-mode} 另外,我们增加的搜索模式配置: ```lua add_requires("cmake::xxx", {configs = {search_mode = "config"}}) add_requires("cmake::xxx", {configs = {search_mode = "module"}}) add_requires("cmake::xxx") -- both ``` 比如指定 config 搜索模式,告诉 cmake 从 `XXXConfig.cmake` 中查找包。 xmake 在查找 cmake 包的时候,内部会自动追加下面的配置。 ```cmake find_package(ABC CONFIG REQUIRED) ``` 这样会同时查找到 pcre、pcre2 等包。 --- --- url: /zh/guide/package-management/using-source-code-packages.md --- # 使用源码包 {#using-source-code-packages} ## 集成 CMake 代码库 2.5.8 版本之后,我们也可以通过 xmake 的包模式直接集成自己项目中带有 CMakeLists.txt 的代码库,而不是通过远程下载安装。 相关 issues: [#1714](https://github.com/xmake-io/xmake/issues/1714) 例如,我们有如下项目结构: ``` ├── foo │   ├── CMakeLists.txt │   └── src │   ├── foo.c │   └── foo.h ├── src │   └── main.c ├── test.lua └── xmake.lua ``` foo 目录下是一个使用 cmake 维护的静态库,而根目录下使用了 xmake 进行维护。我们可以在 xmake.lua 中通过定义 `package("foo")` 包来描述如何构建 foo 代码库。 ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` 其中,我们通过 `set_sourcedir()` 来设置 foo 包的代码目录位置,然后通过 import 导入 `package.tools.cmake` 辅助模块来调用 cmake 构建代码,xmake 会自动获取生成的 libfoo.a 及对应的头文件。 ::: tip 注意 如果仅仅本地源码集成,我们不需要额外设置 `add_urls` 和 `add_versions`。 ::: 关于包的配置描述,详情见:[包描述说明](/zh/guide/package-management/package-distribution.html#define-package-configuration)。 定义完包后,我们就可以通过 `add_requires("foo")` 和 `add_packages("foo")` 来集成使用它了,和集成远程包的方式完全一致。 另外,`on_test` 是可选的。如果想要严格检测包的编译安装是否成功,可以在里面做一些测试。 完整例子见:[Library with CMakeLists](https://github.com/xmake-io/xmake/tree/master/tests/projects/c/library_with_cmakelists) ## 集成 Meson 代码库 xmake 支持集成更多其他构建系统维护的第三方源码库,比如 meson,仅需导入使用 `package.tools.meson` 辅助构建模块调用 meson 来构建它们。 例如,我们从 xmake-repo 仓库中挑选一个使用 meson 构建的包作为例子: ```lua package("harfbuzz") set_sourcedir(path.join(os.scriptdir(), "3rd/harfbuzz")) add_deps("meson") if not is_plat("windows") then add_deps("freetype") end on_load("windows", "linux", "macosx", function (package) if package:config("icu") then package:add("deps", "icu4c") end end) on_install("windows", "linux", "macosx", function (package) local configs = {"-Dtests=disabled", "-Ddocs=disabled", "-Dbenchmark=disabled", "-Dcairo=disabled", "-Dfontconfig=disabled", "-Dglib=disabled", "-Dgobject=disabled"} table.insert(configs, "-Ddefault_library=" .. (package:config("shared") and "shared" or "static")) if package:is_plat("windows") then table.insert(configs, "-Dfreetype=disabled") end import("package.tools.meson").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("hb_buffer_add_utf8", {includes = "harfbuzz/hb.h"})) end) ``` ## 集成 autoconf 代码库 xmake 也可以使用 `package.tools.autoconf` 来本地集成带有 autoconf 维护的第三方代码库。 ```lua package("pcre2") set_sourcedir(path.join(os.scriptdir(), "3rd/pcre2")) add_configs("jit", {description = "Enable jit.", default = true, type = "boolean"}) add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}}) on_load(function (package) local bitwidth = package:config("bitwidth") or "8" package:add("links", "pcre2-" .. bitwidth) package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth) if not package:config("shared") then package:add("defines", "PCRE2_STATIC") end end) on_install("macosx", "linux", "mingw", function (package) local configs = {} table.insert(configs, "--enable-shared=" .. (package:config("shared") and "yes" or "no")) table.insert(configs, "--enable-static=" .. (package:config("shared") and "no" or "yes")) if package:debug() then table.insert(configs, "--enable-debug") end if package:config("pic") ~= false then table.insert(configs, "--with-pic") end if package:config("jit") then table.insert(configs, "--enable-jit") end local bitwidth = package:config("bitwidth") or "8" if bitwidth ~= "8" then table.insert(configs, "--disable-pcre2-8") table.insert(configs, "--enable-pcre2-" .. bitwidth) end import("package.tools.autoconf").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("pcre2_compile", {includes = "pcre2.h"})) end) ``` `package.tools.autoconf` 和 `package.tools.cmake` 模块都可以支持 mingw/cross/iphoneos/android 等交叉编译平台和工具链,xmake 会自动传递对应的工具链进去,用户无需做任何其他操作。 ## 集成 Scons 代码库 xmake 也可以使用 `package.tools.scons` 来本地集成带有 Scons 维护的第三方代码库。 ```lua package("godotcpp") set_sourcedir(path.join(os.scriptdir(), "3rd/godotcpp")) add_deps("scons") add_includedirs("include", "include/core", "include/gen") on_install("linux", "windows", "macosx", "mingw", "cygwin", "iphoneos", "android", "msys", function (package) local configs = {"generate_bindings=yes"} table.insert(configs, "bits=" .. ((package:is_arch("x64") or package:is_arch("x86_64")) and "64" or "32")) if package:is_plat("windows") then io.replace("SConstruct", "/MD", "/" .. package:config("vs_runtime"), {plain = true}) end -- this fixes an error on ios and osx (https://godotengine.org/qa/65616/problems-compiling-gdnative-c-example-on-osx) if package:is_plat("macosx", "iphoneos") then io.replace("SConstruct", "-std=c++14", "-std=c++17", {plain = true}) end -- fix to use correct ranlib, @see https://github.com/godotengine/godot-cpp/issues/510 if package:is_plat("android") then io.replace("SConstruct", [[env['AR'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ar"]], [[env['AR'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ar" env['RANLIB'] = toolchain + "/bin/" + arch_info['tool_path'] + "-ranlib"]], {plain = true}) end import("package.tools.scons").build(package, configs) os.cp("bin/*." .. (package:is_plat("windows") and "lib" or "a"), package:installdir("lib")) os.cp("include/core/*.hpp", package:installdir("include/core")) os.cp("include/gen/*.hpp", package:installdir("include/gen")) os.cp("godot-headers/android", package:installdir("include")) os.cp("godot-headers/arvr", package:installdir("include")) os.cp("godot-headers/gdnative", package:installdir("include")) os.cp("godot-headers/nativescript", package:installdir("include")) os.cp("godot-headers/net", package:installdir("include")) os.cp("godot-headers/pluginscript", package:installdir("include")) os.cp("godot-headers/videodecoder", package:installdir("include")) os.cp("godot-headers/*.h", package:installdir("include")) end) ``` ## 集成 makefile 代码库 ### 使用 Nmake xmake 也可以使用 `package.tools.nmake` 来本地集成带有 nmake 维护的第三方代码库。 nmake.install 会自动绑定当前用户的 msvc 构建环境,确保用户能够顺利调用到 nmake.exe 以及 msbuild 和 cl.exe 等程序。 ```lua package("libxml2") set_sourcedir(path.join(os.scriptdir(), "3rd/libxml2")) add_includedirs("include/libxml2") if is_plat("windows") then add_syslinks("wsock32", "ws2_32") end on_load("windows", function (package) if not package:config("shared") then package:add("defines", "LIBXML_STATIC") end end) on_install("windows", function (package) os.cd("win32") os.vrun("cscript configure.js iso8859x=yes iconv=no compiler=msvc cruntime=/%s debug=%s prefix=\"%s\"", package:config("vs_runtime"), package:debug() and "yes" or "no", package:installdir()) import("package.tools.nmake").install(package, {"/f", "Makefile.msvc"}) os.tryrm(path.join(package:installdir("lib"), "libxml2_a_dll.lib")) if package:config("shared") then os.tryrm(path.join(package:installdir("lib"), "libxml2_a.lib")) else os.tryrm(path.join(package:installdir("lib"), "libxml2.lib")) os.tryrm(path.join(package:installdir("bin"), "libxml2.dll")) end end) on_test(function (package) assert(package:has_cfuncs("xmlNewNode", {includes = {"libxml/parser.h", "libxml/tree.h"}})) end) ``` ### 使用 GnuMake xmake 也可以使用 `package.tools.make` 来本地集成带有 gnumake 维护的第三方代码库。 ```lua package("openssl") set_sourcedir(path.join(os.scriptdir(), "3rd/openssl")) add_links("ssl", "crypto") if is_plat("linux", "cross") then add_syslinks("dl") end on_install("linux", "macosx", function (package) -- https://wiki.openssl.org/index.php/Compilation_and_Installation#PREFIX_and_OPENSSLDIR os.vrun("./config %s --openssldir=\"%s\" --prefix=\"%s\"", package:debug() and "--debug" or "", package:installdir(), package:installdir()) import("package.tools.make").install(package) end) on_test(function (package) assert(package:has_cfuncs("SSL_new", {includes = "openssl/ssl.h"})) end) ``` ::: tip 注意 我们也可以直接使用 `os.vrunv("make", {})` 来调用 make/gmake 程序来构建库。 ::: ## 集成 Gn 代码库 我们也可以使用 `package.tools.gn` 来本地集成带有 GN 维护的第三方代码库。 ```lua package("skia") set_sourcedir(path.join(os.scriptdir(), "3rd/openssl")) add_deps("gn", "ninja") add_deps("python", {kind = "binary"}) on_install("linux", "macosx", "windows", function (package) import("package.tools.gn").install(package) end) on_test(function (package) -- TODO end) ``` 这里有完整的脚本例子:[Skia with GN](https://github.com/xmake-io/xmake-repo/blob/master/packages/s/skia/xmake.lua) --- --- url: /zh/guide/package-management/using-third-party-packages.md --- # 使用第三方依赖包 {#using-third-party-packages} 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") ``` 我们也可以为包添加别名,简化 `add_packages` 的使用: ```lua add_requires("vcpkg::zlib", {alias = "zlib"}) add_requires("vcpkg::pcre2", {alias = "pcre2"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "pcre2") ``` 如果 vcpkg 包带有可选特性,我们也可以直接使用 vcpkg 的语法格式 `packagename[feature1,feature2]` 来安装包。 例如: ```lua add_requires("vcpkg::boost[core]") ``` v2.6.3 之后,xmake 支持 vcpkg 的新清单模式,通过它可以支持 vcpkg 包的版本选择,例如: ```lua add_requires("vcpkg::zlib 1.2.11") add_requires("vcpkg::fmt >=8.0.1", {configs = {baseline = "50fd3d9957195575849a49fa591e645f1d8e7156"}}) add_requires("vcpkg::libpng", {configs = {features = {"apng"}}}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("vcpkg::zlib", "vcpkg::fmt", "vcpkg::libpng") ``` v2.6.8 之后,还可以额外配置私有仓库,仅在清单模式下有效。 ```lua local registries = { { kind = "git", repository = "https://github.com/SakuraEngine/vcpkg-registry", baseline = "e0e1e83ec66e3c9b36066f79d133b01eb68049f7", packages = { "skrgamenetworkingsockets" } } } add_requires("vcpkg::skrgamenetworkingsockets >=1.4.0+1", {configs = {registries = registries}}) ``` ## 使用 conan 的依赖包 ```lua add_requires("conan::zlib/1.2.11", {alias = "zlib", debug = true}) add_requires("conan::openssl/1.1.1g", {alias = "openssl", configs = {options = "OpenSSL:shared=True"}}) target("test") set_kind("binary") add_files("src/*.c") add_packages("openssl", "zlib") ``` 执行xmake进行编译后: ```sh 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 (debug) -> conan::openssl/1.1.1g please input: y (y/n) => installing conan::zlib/1.2.11 .. ok => installing conan::openssl/1.1.1g .. ok [ 0%]: cache compiling.release src/main.c [100%]: linking.release test ``` 自定义 conan/settings 配置: ```lua add_requires("conan::poco/1.10.0", {alias = "poco", configs = {settings = {"compiler=gcc", "compiler.libcxx=libstdc++11"}}}) ``` 其他一些 conan 相关配置项: ``` { build = {description = "use it to choose if you want to build from sources.", default = "missing", values = {"all", "never", "missing", "outdated"}}, remote = {description = "Set the conan remote server."}, options = {description = "Set the options values, e.g. OpenSSL:shared=True"}, imports = {description = "Set the imports for conan."}, settings = {description = "Set the build settings for conan."}, build_requires = {description = "Set the build requires for conan.", default = "xmake_generator/0.1.0@bincrafters/testing"} } ``` ## 使用 conda 的依赖包 ```lua add_requires("conda::zlib 1.2.11", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` ## 使用 pacman 的依赖包 我们既支持 archlinux 上的 pacman 包安装和集成,也支持 msys2 上 pacman 的 mingw x86\_64/i386 包安装和集成。 ```lua add_requires("pacman::zlib", {alias = "zlib"}) add_requires("pacman::libpng", {alias = "libpng"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libpng") ``` archlinux 上只需要: ```sh xmake ``` msys2 上安装 mingw 包,需要指定到 mingw 平台: ```sh xmake f -p mingw -a [x86_64|i386] xmake ``` ## 使用 clib 的依赖包 clib是一款基于源码的依赖包管理器,拉取的依赖包是直接下载对应的库源码,集成到项目中编译,而不是二进制库依赖。 其在 xmake 中集成也很方便,唯一需要注意的是,还需要自己在 xmake.lua 中引用对应库的源码,例如: ```lua add_requires("clib::clibs/bytes@0.0.4", {alias = "bytes"}) target("test") set_kind("binary") add_files("clib/bytes/*.c") add_files("src/*.c") add_packages("bytes") ``` ## 使用 dub/dlang 的依赖包 xmake 也支持 dlang 的 dub 包管理器,可集成 dlang 的依赖包来使用。 ```lua add_rules("mode.debug", "mode.release") add_requires("dub::log 0.4.3", {alias = "log"}) add_requires("dub::dateparser", {alias = "dateparser"}) add_requires("dub::emsi_containers", {alias = "emsi_containers"}) add_requires("dub::stdx-allocator", {alias = "stdx-allocator"}) add_requires("dub::mir-core", {alias = "mir-core"}) target("test") set_kind("binary") add_files("src/*.d") add_packages("log", "dateparser", "emsi_containers", "stdx-allocator", "mir-core") ``` ## 使用 ubuntu/apt 的依赖包 v2.5.4 之后的版本支持使用 apt 集成依赖包,也会自动查找 Ubuntu 系统上已安装的包。 ```lua add_requires("apt::zlib1g-dev", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` ## 使用 gentoo/portage 的依赖包 v2.5.4 之后的版本支持使用 Portage 集成依赖包,也会自动查找 Gentoo 系统上已安装的包。 ```lua add_requires("portage::libhandy", {alias = "libhandy"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("libhandy") ``` ## 使用 nimble 的依赖包 v2.5.8 之后支持集成 nimble 包管理器中的包,但目前仅用于 nim 项目,因为它并没有提供二进制包,而是直接安装的 nim 代码包。 ```lua add_requires("nimble::zip >1.3") target("test") set_kind("binary") add_files("src/main.nim") add_packages("nimble::zip") ``` ## 使用 cargo 的依赖包 Cargo 依赖包主要用于 rust 项目的集成,例如: ```lua add_rules("mode.release", "mode.debug") add_requires("cargo::base64 0.13.0") add_requires("cargo::flate2 1.0.17", {configs = {features = "zlib"}}) target("test") set_kind("binary") add_files("src/main.rs") add_packages("cargo::base64", "cargo::flate2") ``` 不过,我们也可以在 C++ 中通过 cxxbridge 的方式,调用 Rust 库接口,来变相复用所有的 Rust 包。 完整例子见:[Call Rust in C++](https://github.com/xmake-io/xmake/tree/dev/tests/projects/rust/cxx_call_rust_library) ## 使用 NuGet 的依赖包 2.9.7 之后,我们也支持从 dotnet/nuget 中,获取 native 库并快速集成。 ```lua add_requires("nuget::zlib_static", {alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib") ``` --- --- url: /zh/guide/package-management/using-system-packages.md --- # 使用系统包 {#using-system-packages} ## 查找使用系统包 Xmake 对远程包和系统包的使用进行了统一,全部使用 `add_requires("zlib")` 接口来描述集成。默认配置下,它会优先从系统上查找库,如果没有,则会自动下载并安装集成。 如果我们只想查找并使用系统库,不想远程下载,可以这样配置: ```lua add_requires("zlib", {system = true}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` 通过 `{system = true}` 就可以强制禁用远程下载,此时它就等价于 Xmake/CMake 的 `find_package`,但更简单易用,并且与远程包的使用方式完全一致。 默认情况下,如果找不到系统库,则会提示失败。如果这个包是可选的,可以额外配置 `{optional = true}` 选项。 ```lua add_requires("zlib", {system = true, optional = true}) ``` ## 查找包快速测试 我们可以使用下面的命令,快速检测系统上指定的包信息: ```sh $ xmake l find_package x264 { links = { "x264" }, linkdirs = { "/usr/local/Cellar/x264/r2699/lib" }, version = "0.148.2699 a5e06b9", includedirs = { "/usr/local/Cellar/x264/r2699/include" } } ``` 我们也可以追加第三方包管理器前缀来测试: ```sh xmake l find_package conan::OpenSSL/1.0.2g ``` ::: tip 注意 find\_package 命令如果在带有 xmake.lua 的工程目录下执行,是会有缓存的。如果查找失败,下次查找也会使用缓存的结果。如果要每次强制重新检测,请切换到非工程目录下执行上述命令。 ::: --- --- url: /zh/posts/add-package-and-autocheck.md --- xmake将依赖库、依赖头文件、依赖类型、依赖接口统一用 option 选项机制进行了封装,更在上一层引入package包的机制,使得添加和检测依赖更加的模块化,简单化。。。 下面通过一个具体实例,来看下xmake的包机制怎么使用。。 假如你现在的工程已经有了两个包:zlib.pkg,polarssl.pkg(如何构建包,后续会详细说明,现在可以参考[TBOX依赖包](https://github.com/waruqi/tbox/tree/master/pkg)下已有包的例子),你的工程目录结构如下: ``` demo - xmake.lua - src main.c - pkg zlib.pkg polarssl.pkg ``` 那么你可以修改xmake.lua来使用上述的两个依赖包: ```lua -- 添加依赖包目录,之后添加需要的包,都会从这个目录里面查找 add_packagedirs("pkg") -- 添加目标 target("demo") -- 设置程序类型为二进制可执行程序 set_kind("binary") -- 添加源代码文件 add_files("src/*.c") -- 通过option机制添加polarssl、zlib包,如果检测通过,会去自动链接它 -- 第一次执行xmake config或者xmake编译的时候会去自动检测它,然后缓存配置 -- 如果要重新检测,则可以执行 xmake config -c清除原有配置,重新配置所有。。。 add_options("polarssl", "zlib") -- 设置自动生成的配置头文件,如果mysql检测通过,会生成CONFIG_PACKAGE_HAVE_MYSQL开关 set_config_h("$(buildir)/config.h") -- 设置config.h宏开关的前缀: CONFIG_xxxx set_config_h_prefix("CONFIG") -- 添加头文件搜索目录,这里为了搜索到config.h add_includedirs("$(buildir)") ``` 接下来是代码里面怎么去使用它: ```c #include // 包含自动生成的config.h头文件 // 搜索路径设置在./build下面 #include "config.h" // 如果当前平台存在zlib,那么使用它 #ifdef CONFIG_PACKAGE_HAVE_ZLIB # include "zlib/zlib.h" #endif // 如果当前平台存在polarssl,那么使用它 #ifdef CONFIG_PACKAGE_HAVE_POLARSSL # include "polarssl/polarssl.h" #endif int main(int argc, char** argv) { printf("hello world!\n"); return 0; } ``` 上面就是一个包使用的最简单的例子,下面我们来看下具体这个zlib.pkg是怎么生成的: 如果这个包是你自己的项目xxx开发的,那么你只需要执行xmake p进行打包,自动会在./build目录下生成一个xxx.pkg的包,你直接在其他项目中使用就行了。。。 如果是第三方的库,那么你需要自己去构建它,但是也很方便,实在不行你可以参考已有的[TBOX依赖包](https://github.com/waruqi/tbox/tree/master/pkg)中一些包,做修改就行了。。。 一个pkg包的目录结构: ``` zlib.pkg - inc(头文件目录,可选) - zlib/zlib.h - lib(链接库目录,可选) - linux/i386/libz.a - windows/i386/zlib.lib - xmake.lua(包描述文件) ``` 其中 inc、lib是可选的,具体逻辑还是在xmake.lua进行描述,xmake默认生成的包逻辑,是会优先去检测zlib.pkg目录有没有当前可用的库和头文件,如果检测不通过,才会去检测系统平台的。。。 当然你也可以自己修改检测逻辑,不一定非得这么来,你只需要根据自己的需求描述xxx.pkg/xmake.lua文件就行了。。。 下面看下我这里提供的zlib.pkg/xmake.lua描述逻辑: ```lua -- 添加一个zlib包自动配置选项 option("zlib") -- 设置是否在xmake f -h配置菜单中显示 -- 如果你想让你的包在工程项目中,可以提示用户手动禁用,那么就启用他吧 set_showmenu(true) -- 在xmake f -h中显示相关描述信息 set_description("The mysql package") -- 如果检测通过,定义宏开关到config.h add_defines_h_if_ok("$(prefix)_PACKAGE_HAVE_ZLIB") -- 检测链接 add_links("z") -- 添加检测的链接库目录,这里设置优先检测zlib.pkg/lib/下相关平台是否存在链接库,然后再去检测系统的 -- 如果这个不去设置,xmake只能检测一些系统目录下的链接库,例如:/usr/lib, /usr/local/lib -- 如果常用系统目录下检测不到,但是你又装了这个库,你可以自己设定检测的搜索目录 add_linkdirs("lib/$(plat)/$(arch)") -- 检测 #include "zlib/zlib.h" 是否能编译通过 add_cincludes("zlib/zlib.h") -- 添加一些检测的头文件目录,默认会在zlib.pkg/inc进行搜索,当然你也可以指定其他目录 add_includedirs("inc/$(plat)", "inc") ``` 只要描述好xxx.pkg/xmake.lua, 一个包就能被xmake使用,并进行自动检测,其中利用的就是xmake的option机制,当然在包里面不仅仅可以检测依赖库和头文件,你也可以检测是否存在某些需要的接口、类型定义等等。。 而且检测机制完全采用lua语法,支持if条件逻辑,你可以针对一些特定的平台,做一些特别处理,使得你的包更加的通用。 例如下面这个基础包base.pkg的描述: ```lua -- 基础包base.pkg option("base") -- 如果当前为windows平台,检测ws2_32链接库依赖 if os("windows") then add_links("ws2_32") -- 如果是其他平台,检测-lm,-ldl,-lpthread依赖(由于都是些系统库,这里就没有设置搜索目录) else add_links("m", "dl", "pthread") end ``` 如果你的包只是通过xmake.lua来描述,没有其他文件目录,那么你也可以把你的包xmake.lua的描述内容,直接嵌入到工程描述文件xmake.lua中, 这两者原本都是通用的,说白了 `add_packagedirs("pkg")` 的机制,就是调用工程描述api:`add_subdirs("pkg/*")`进行添加子工程的过程。。而xxx.pkg说白了就是一个子工程描述文件而已。。。 如果你想在你的包检测中增加对接口的检测,那么只需要用: * `add_cfuncs` * `add_cxxfuncs` * `add_ctypes` * `add_cxxtypes` 就行了 所以利用包的机制,可以让你的不同项目最大化重用你的依赖环境。。是个非常有用的功能。。 ``` ``` --- --- url: /zh/api/description/global-interfaces.md --- # 全局接口 {#global-interfaces} 全局接口影响整个工程描述,被调用后,后面被包含进来的所有子`xmake.lua`都会受影响。 ## includes ### 添加子工程文件和目录 {#add-sub-project-and-configurations} #### 函数原型 ::: tip API ```lua includes(paths: , ..., { rootdir = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | paths | 子工程文件或目录路径,支持模式匹配如 "\*\*/xmake.lua" | | ... | 可变参数,可传入多个路径 | | rootdir | 相对路径解析的根目录,可选 | #### 用法说明 #### 引入子目录配置 我们能够使用此接口添加工程子文件 (xmake.lua) 或者带有 xmake.lua 的工程子目录。 ``` projectdir - subdirs - xmake.lua - src ``` 添加子工程目录: ```lua includes("subdirs") target("test") set_kind("binary") add_files("src/*.c") ``` 或者添加子工程文件: ```lua includes("subdirs/xmake.lua") target("test") set_kind("binary") add_files("src/*.c") ``` #### 递归引入子配置 {#recursively-add-configurations} 我们也可以通过模式匹配的方式,递归添加多个工程子目录文件: ```lua includes("**/xmake.lua") target("test") set_kind("binary") add_files("src/*.c") ``` #### 引入内置的辅助配置 {#add-helper-configurations} 2.8.5 版本可以 includes 包含内置的一些辅助配置脚本,例如: ```lua includes("@builtin/check") ``` 会引入内置提供的一些检测辅助接口。 还有 ```lua includes("@builtin/qt") ``` 会引入一些内置的 Qt 相关辅助接口。 其中 `@builtin` 是告诉 xmake 从内置的 includes 目录中引入配置脚本。 也就是这个路径下的配置文件:[includes](https://github.com/xmake-io/xmake/tree/master/xmake/includes) 我们可以向上面那样,按目录整个引入,也可以引入单个配置文件,例如: ```lua includes("@builtin/check/check_cfuncs.lua") ``` 仅仅引入 check 目录下 check\_cfuncs 相关的辅助脚本。 而通过 `@builtin` 我们就能很好的区分是引入当前用户工程目录下的文件,还是 xmake 安装目录下的内置文件。 #### 作用域说明 {#scope-description} includes 引入的配置是按树状层级结构来继承生效的,也就是当前 xmake.lua 中的全局配置,会对所有 includes 的子 xmake.lua 配置生效,例如: ``` projectdir - xmake.lua - foo/xmake.lua - bar/xmake.lua ``` 上面的组织结构中,在 `projectdir/xmake.lua` 中的 includes 的所有配置,在 `foo/xmake.lua` 和 `bar/xmake.lua` 中都是可以访问的,但是反过来不行。 ```lua includes("foo") includes("bar") target("test") add_files("src/*.c") ``` 就比如上面的情况,如果引入的 `foo/xmake.lua` 中有全局的 `add_defines` 配置,是无法对 test target 生效的,因为 foo/xmake.lua 属于子配置,无法影响到父配置。 ::: tip 注意 这种作用域隔离,能规避很多隐藏的配置冲突和作用域污染,在嵌套层级过多的工程配置中,隐式的全局引入,会导致很多的问题。 ::: #### 模块化复用配置 {#modular-reusable} 那如果我想模块化复用配置,应该怎么做呢?只需要通过 function 去封装下需要复用的配置就行了,例如: ```lua [foo/xmake.lua] function add_foo_configs() add_defines("FOO") -- ... end ``` ```lua [bar/xmake.lua] function add_bar_configs() add_defines("BAR") -- ... end ``` ```lua [xmake.lua] includes("foo") includes("bar") target("test1") add_files("src/*.c") add_foo_configs() target("test2") add_files("src/*.c") add_bar_configs() ``` 这种方式,不仅可以规避隐式的全局引入导致的配置冲突,而且还能支持按 target 粒度分别配置,不仅支持配置模块复用,而且更加地灵活。 如果想要全局生效,也只需要移到全局跟作用域就行。 ```lua [xmake.lua] includes("foo") includes("bar") add_foo_configs() add_bar_configs() target("test1") add_files("src/*.c") target("test2") add_files("src/*.c") ``` ::: tip 注意 另外,target 的域配置是可以重复进入追加配置的,很多情况下,都不需要封装 function,简简单单 includes 组织配置,重复进入 target 配置域在不同 xmake.lua 中更新 target 配置即可。 ::: ## set\_project ### 设置工程名 #### 函数原型 ::: tip API ```lua set_project(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 工程名称字符串 | #### 用法说明 设置工程名,在doxygen自动文档生成插件、工程文件生成插件中会用到,一般设置在xmake.lua的最开头,当然放在其他地方也是可以的 ```lua -- 设置工程名 set_project("tbox") -- 设置工程版本 set_version("1.5.1") ``` ## set\_version ### 设置工程版本 #### 函数原型 ::: tip API ```lua set_version(version: , { build = , soname = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 工程版本字符串,如 "1.5.1" | | build | 构建版本字符串,支持时间格式如 "%Y%m%d%H%M" | | soname | 动态库兼容性控制的 soname 版本,可为字符串或布尔值 | #### 用法说明 设置项目版本,可以放在 xmake.lua 任何地方,一般放在最开头,例如: ```lua set_version("1.5.1") ``` 2.1.7 版本支持 buildversion 的配置: ```lua set_version("1.5.1", {build = "%Y%m%d%H%M"}) ``` 我们也能够添加版本宏定义到头文件,请参考:[add\_configfiles](/zh/api/description/project-target#add-configfiles) :::tip 注意 我们可以全局设置版本,但现在我们也可以在 target 域去单独设置它。 ::: 2.8.2 版本新增了 soname 版本支持,用于控制 so/dylib 动态库的版本兼容性控制。 我们可以配置 soname 的版本后缀名称,xmake 会在编译、安装动态库的时候,自动生成符号链接,执行指定版本的动态库。 例如,如果我们配置: ```lua set_version("1.0.1", {soname = true}) ``` xmake 会自动解析版本号的 major 版本作为 soname 版本,生成的结构如下: ``` └── lib ├── libfoo.1.0.1.dylib ├── libfoo.1.dylib -> libfoo.1.0.1.dylib └── libfoo.dylib -> libfoo.1.dylib ``` 当然,我们也可以指定 soname 到特定的版本命名: ```lua set_version("1.0.1", {soname = "1.0"}) -> libfoo.so.1.0, libfoo.1.0.dylib set_version("1.0.1", {soname = "1"}) -> libfoo.so.1, libfoo.1.dylib set_version("1.0.1", {soname = "A"}) -> libfoo.so.A, libfoo.A.dylib set_version("1.0.1", {soname = ""}) -> libfoo.so, libfoo.dylib ``` 而如果没设置 soname,那么默认不开启 soname 版本兼容控制: ```lua set_version("1.0.1") -> libfoo.so, libfoo.dylib ``` ## set\_xmakever ### 设置最小xmake版本 #### 函数原型 ::: tip API ```lua set_xmakever(version: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 最小要求的 xmake 版本字符串,如 "2.1.0" | #### 用法说明 用于处理xmake版本兼容性问题,如果项目的`xmake.lua`,通过这个接口设置了最小xmake版本支持,那么用户环境装的xmake低于要求的版本,就会提示错误。 一般情况下,建议默认对其进行设置,这样对用户比较友好,如果`xmake.lua`中用到了高版本的api接口,用户那边至少可以知道是否因为版本不对导致的构建失败。 设置如下: ```lua -- 设置最小版本为:2.1.0,低于此版本的xmake编译此工程将会提示版本错误信息 set_xmakever("2.1.0") ``` ## add\_moduledirs ### 添加模块目录 #### 函数原型 ::: tip API ```lua add_moduledirs(dirs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dirs | 模块目录路径字符串或数组 | | ... | 可变参数,可传入多个目录路径 | #### 用法说明 xmake内置的扩展模块都在`xmake/modules`目录下,可通过[import](/zh/api/scripts/builtin-modules/import)来导入他们,如果自己在工程里面实现了一些扩展模块, 可以放置在这个接口指定的目录下,import也就会能找到,并且优先进行导入。 ## add\_plugindirs ### 添加插件目录 #### 函数原型 ::: tip API ```lua add_plugindirs(dirs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dirs | 插件目录路径字符串或数组 | | ... | 可变参数,可传入多个目录路径 | #### 用法说明 xmake内置的插件都是放在`xmake/plugins`目录下,但是对于用户自定义的一些特定工程的插件,如果不想放置在xmake安装目录下,那么可以在`xmake.lua`中进行配置指定的其他插件路径。 ```lua -- 将当前工程下的plugins目录设置为自定义插件目录 add_plugindirs("$(projectdir)/plugins") ``` 这样,xmake在编译此工程的时候,也就加载这些插件。 ## get\_config ### 获取给定的配置值 #### 函数原型 ::: tip API ```lua get_config(name: ): ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置选项名称字符串 | #### 用法说明 此接口从2.2.2版本开始引入,用于快速获取给定的配置值,可用于描述域。 ```lua if get_config("myconfig") == "xxx" then add_defines("HELLO") end ``` ::: tip 提示 此接口不仅能够获取通过[option](/zh/api/description/configuration-option#option)定义的自定义配置选项值,同时还能获取内置的全局配置、本地配置。 ::: ## set\_config ### 设置给定的默认配置值 #### 函数原型 ::: tip API ```lua set_config(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置选项名称字符串 | | value | 配置值,可为字符串、布尔值或数字 | #### 用法说明 此接口从2.2.2版本开始引入,用于快速在xmake.lua中设置一个默认配置值,仅用于描述域。 之前很多配置,包括编译工具链,构建目录等只能通过`$ xmake f --name=value`的方式来配置,如果我们想写死在xmake.lua提供一个默认值,就可以通过下面的方式来配置: ```lua set_config("name", "value") set_config("builddir", "other/builddir") set_config("cc", "gcc") set_config("ld", "g++") ``` 不过,我们还是可以通过`$ xmake f --name=value`的方式,去修改xmake.lua中的默认配置。 ## add\_requires ### 添加需要的依赖包 #### 函数原型 ::: tip API ```lua add_requires(packages: , ..., { optional = , system = , verify = , debug = , private = , configs =
, alias = , ... = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | packages | 包名称字符串或数组,支持语义版本如 "tbox 1.6.\*" | | ... | 可变参数,可传入多个包名称 | | optional | 设置为可选包,布尔值 | | system | 禁用系统包检测,布尔值 | | verify | 禁用包校验,布尔值 | | debug | 使用调试版本包,布尔值 | | private | 作为私有包使用,布尔值 | | configs | 包特定配置表 | | alias | 包别名 | | ... | 其他包特定配置参数 | #### 用法说明 xmake的依赖包管理是完全支持语义版本选择的,例如:"~1.6.1",对于语义版本的具体描述见: #### 语义版本 ```lua add_requires("tbox 1.6.*", "pcre 8.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") ``` 默认,没设置版本号,xmake 会选取最近版本的包,等价于 `add_requires("zlib latest")` #### 分支选择 这会使用已知的最新版本包,或者是master分支的源码编译的包,如果当前包有git repo地址,我们也能指定特定分支版本: ```lua add_requires("tbox master") add_requires("tbox dev") ``` 如果指定的依赖包当前平台不支持,或者编译安装失败了,那么xmake会编译报错,这对于有些必须要依赖某些包才能工作的项目,这是合理的。 但是如果有些包是可选的依赖,即使没有也可以正常编译使用的话,可以设置为可选包: #### Git commit 选择 2.6.5 版本,我们可以对 git 维护的包直接指定 git commit 来选择版本。 ```lua add_requires("tbox e807230557aac69e4d583c75626e3a7ebdb922f8") ``` #### 可选包 ```lua add_requires("zlib", {optional = true}) ``` #### 禁用系统包 默认的设置,xmake会去优先检测系统库是否存在(如果没设置版本要求),如果用户完全不想使用系统库以及第三方包管理提供的库,那么可以设置: ```lua add_requires("zlib", {system = false}) ``` #### 禁用包校验 默认包安装,对于下载的包都是会去自动校验完整性,避免被篡改,但是如果安装一些未知新版本的包,就不行了。 用户可以通过 `{verify = false}` 强行禁用包完整性校验来临时安装他们(但通常不推荐这么做)。 ```lua add_requires("zlib", {verify = false}) ``` #### 使用调试包 如果我们想同时源码调试依赖包,那么可以设置为使用debug版本的包(当然前提是这个包支持debug编译): ```lua add_requires("zlib", {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) ``` #### 作为私有包使用 如果这个包,我们仅仅用于包定义,不想对外默认导出 links/linkdirs 信息,可以作为私有包提供。 这通常对于做包时候,很有用。 ```lua package("test") add_deps("zlib", {private = true}) on_install(function (package) local zlib = package:dep("zlib"):fetch() -- TODO end) ``` 如果自己定义的一个 test 包,私有依赖一个 zlib 包,等待 zlib 安装完成后,获取里面的包文件信息做进一步处理安装,但是 zlib 包本身不会再对外导出 links/linkdirs。 尽管,`add_requires` 也支持这个选项,但是不对外导出 links/linkdirs,所以通常不会去这么用,仅仅对于做包很有帮助。 #### 使用动态库 默认的包安装的是静态库,如果要启用动态库,可以配置如下: ```lua add_requires("zlib", {configs = {shared = true}}) ``` :::tip 注意 当然,前提是这个包的定义里面,有对 `package:config("shared")` 判断处理,官方 xmake-repo 仓库里面,通常都是严格区分支持的。 ::: #### 禁用 pic 支持 默认安装的 linux 包,都是开启 pic 编译的,这对于动态库中依赖静态库非常有用,但如果想禁用 pic,也是可以的。 ```lua add_requires("zlib", {configs = {pic = false}}) ``` #### vs runtime 设置 默认安装的 windows 包是采用 msvc/MT 编译的,如果要切换到 MD,可以配置如下: ```lua add_requires("zlib", {configs = {vs_runtime = "MD"}}) ``` 另外,还支持:MT, MTd, MD, MDd 四种选项。 如果依赖的包很多,每个配置切换一遍非常的麻烦,我们也可以通过 `set_runtimes` 全局设置切换,对所有依赖包生效。 ```lua set_runtimes("MD") add_requires("zlib", "pcre2", "mbedtls") ``` #### 特定配置包 某些包在编译时候有各种编译选项,我们也可以传递进来: ```lua add_requires("boost", {configs = {context = true, coroutine = true}}) ``` 比如上面,安装的 boost 包,是启用了它内部的一些子模块特性(带有协程模块支持的包)。 当然,具体支持哪些配置,每个包都是不同的,可以通过 `xmake require --info boost` 命令查看里面的 configs 部分列表。 因为,每个包定义里面,都会有自己的配置选项,并且通过 `package:config("coroutine")` 在安装时候去判断启用它们。 #### 安装第三方管理器的包 目前支持安装下面这些第三方包管理器中包。 * Conan (conan::openssl/1.1.1g) * Conda (conda::libpng 1.3.67) * Vcpkg (vcpkg::ffmpeg) * Homebrew/Linuxbrew (brew::pcre2/libpcre2-8) * Pacman on archlinux/msys2 (pacman::libcurl) * Apt on ubuntu/debian (apt::zlib1g-dev) * Clib (clib::clibs/bytes@0.0.4) * Dub (dub::log 0.4.3) * Portage on Gentoo/Linux (portage::libhandy) 例如添加conan的依赖包: ```lua add_requires("conan::zlib/1.2.11", {alias = "zlib", debug = true}) add_requires("conan::openssl/1.1.1g", {alias = "openssl", configs = {options = "OpenSSL:shared=True"}}) target("test") set_kind("binary") add_files("src/*.c") add_packages("openssl", "zlib") ``` 执行xmake进行编译后: ```sh 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 (debug) -> conan::openssl/1.1.1g please input: y (y/n) => installing conan::zlib/1.2.11 .. ok => installing conan::openssl/1.1.1g .. ok [ 0%]: cache compiling.release src/main.c [100%]: linking.release test ``` 关于这个的完整介绍和所有第三方包的安装使用,可以参考文档:[第三方依赖包安装](/zh/guide/package-management/using-third-party-packages)。 #### 另一种简化的配置语法 我们通常使用的常用配置语法: ```lua add_requires("boost >=1.78.0", {configs = {iostreams = true, system = true, thread = true}}) ``` 对于大部分 boolean 配置,我们可以通过下面的写法,去简化配置。 ```lua add_requires("boost[iostreams,system,thread] >=1.78.0") ``` 这对于 `xrepo install` 独立 cli 命令下带复杂配置的安装,会省事不少,用户可以根据自己的喜好需求,选择使用。 ```sh xrepo install boost[iostreams,system,thread] ``` 另外,除了 boolean 配置,还支持 string 和 array 配置值。boolean 值,也可以设置 `=n/y` 去禁用和启用。 ```lua add_requires("boost[iostreams,system,thread,key=value] >=1.78.0") add_requires("boost[iostreams=y,thread=n] >=1.78.0") add_requires("ffmpeg[shared,debug,codecs=[foo,bar,zoo]]") ``` ## add\_requireconfs ### 设置指定依赖包的配置 #### 函数原型 ::: tip API ```lua add_requireconfs(packages: , ..., { configs =
, override = , version = , debug = , ... = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | packages | 包名称字符串或数组,支持模式匹配如 "\*" 或 "libpng.zlib" | | ... | 可变参数,可传入多个包名称 | | configs | 包配置表 | | override | 覆盖现有配置,布尔值 | | version | 包版本字符串 | | debug | 使用调试版本,布尔值 | | ... | 其他包特定配置参数 | #### 用法说明 这是 v2.5.1 之后的版本新增的接口,我们可以用它来对 `add_requires()` 定义的包和它的依赖包的配置进行扩充和改写,它有下面几种用法。 #### 扩充指定包的配置 这是基本用法,比如我们已经通过 `add_requires("zlib")` 声明了一个包,想要在后面对这个 zlib 的配置进行扩展,改成动态库编译,可以通过下面的方式配置。 ```lua add_requires("zlib") add_requireconfs("zlib", {configs = {shared = true}}) ``` 它等价于 ```lua add_requires("zlib", {configs = {shared = true}}) ``` #### 设置通用的默认配置 上面的用法,我们还看不出有什么实际用处,但如果依赖多了就能看出效果了,比如下面这样: ```lua add_requires("zlib", {configs = {shared = true}}) add_requires("pcre", {configs = {shared = true}}) add_requires("libpng", {configs = {shared = true}}) add_requires("libwebp", {configs = {shared = true}}) add_requires("libcurl", {configs = {shared = false}}) ``` 是不是非常繁琐,如果我们用上 `add_requireconfs` 来设置默认配置,就可以极大的简化成下面的配置: ```lua add_requireconfs("*", {configs = {shared = true}}) add_requires("zlib") add_requires("pcre") add_requires("libpng") add_requires("libwebp") add_requires("libcurl", {configs = {shared = false}}) ``` 上面的配置,我们通过 `add_requireconfs("*", {configs = {shared = true}})` 使用模式匹配的方式,设置所有的依赖包默认走动态库编译安装。 但是,我们又通过 `add_requires("libcurl", {configs = {shared = false}})` 将 libcurl 进行了特殊配置,强制走静态库编译安装。 最终的配置结果为:zlib/pcre/libpng/libwebp 是 shared 库,libcurl 是静态库。 我们通过模式匹配的方式,可以将一些每个包的常用配置都放置到统一的 `add_requireconfs` 中去预先配置好,极大简化每个 `add_requires` 的定义。 :::tip 注意 默认情况下,对于相同的配置,xmake 会优先使用 add\_requires 中的配置,而不是 add\_requireconfs。 ::: 如果 `add_requires("zlib 1.2.11")` 中设置了版本,就会优先使用 add\_requires 的配置,完全忽略 add\_requireconfs 里面的版本配置,当然我们也可以通过 override 来完全重写 `add_requires` 中指定的版本。 ```lua add_requires("zlib 1.2.11") add_requireconfs("zlib", {override = true, version = "1.2.10"}) ``` #### 改写包依赖配置 其实 `add_requireconfs` 最大的用处是可以让用户改写安装包的特定依赖包的配置。 什么意思呢,比如我们项目中集成使用 libpng 这个包,并且使用了动态库版本,但是 libpng 内部依赖的 zlib 库其实还是静态库版本。 ```lua add_requires("libpng", {configs = {shared = true}}) ``` 那如果我们想让 libpng 依赖的 zlib 包也改成动态库编译,应该怎么配置呢?这就需要 `add_requireconfs` 了。 ```lua add_requires("libpng", {configs = {shared = true}}) add_requireconfs("libpng.zlib", {configs = {shared = true}}) ``` 通过 `libpng.zlib` 依赖路径的写法,指定内部某个依赖,改写内部依赖配置。 如果依赖路径很深,比如 `foo -> bar -> xyz` 的依赖链,我们可以写成:`foo.bar.xyz` 我们也可以改写 libpng 依赖的内部 zlib 库版本: ```lua add_requires("libpng") add_requireconfs("libpng.zlib", {override = true, version = "1.2.10"}) ``` #### 级联依赖的模式匹配 如果一个包的依赖非常多,且依赖层次也很深,怎么办呢,比如 libwebp 这个包,它的依赖有: ``` libwebp - libpng - zlib - cmake - libjpeg - libtiff - zlib - giflib - cmake ``` 如果我想改写 libwebp 里面的所有的依赖库都加上特定配置,那么挨个配置,就会非常繁琐,这个时候就需要 `add_requireconfs()` 的递归依赖模式匹配来支持了。 ```lua add_requires("libwebp") add_requireconfs("libwebp.**|cmake", {configs = {cxflags = "-DTEST"}}) ``` 上面的配置,我们将 libwebp 中所以的库依赖就额外加上了 `-DTEST` 来编译,但是 cmake 依赖属于构建工具依赖,我们可以通过 `|xxx` 的方式排除它。 这里的模式匹配写法,与 `add_files()` 非常类似。 我们在给几个例子,比如这回我们只改写 libwebp 下单级的依赖配置,启用调试库: ```lua add_requires("libwebp") add_requireconfs("libwebp.*|cmake", {debug = true}) ``` ## add\_repositories ### 添加依赖包仓库 #### 函数原型 ::: tip API ```lua add_repositories(repos: , ..., { rootdir = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | repos | 仓库名称和URL字符串或数组,格式:"name url" | | ... | 可变参数,可传入多个仓库 | | rootdir | 相对路径解析的根目录,可选 | #### 用法说明 如果需要的包不在官方仓库[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)项目,里面就内置了一个私有仓库。 注:其中 myrepo 是 xmake 命令执行目录的相对路径,它不会自动根据配置文件所在目录自动转换,如果想要设置到相对于当前 xmake.lua 文件的路径,可以通过 rootdir 参数指定。 ```lua add_repositories("my-repo myrepo", {rootdir = os.scriptdir()}) ``` 不过这个参数设置只有 v2.5.7 以上版本才支持。 ## set\_defaultplat ### 设置默认的编译平台 #### 函数原型 ::: tip API ```lua set_defaultplat(platform: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | platform | 默认编译平台名称,如 "iphoneos", "windows" | #### 用法说明 v2.5.6 以上版本才支持,用于设置工程默认的编译平台,如果没有设置,默认平台跟随当前系统平台,也就是 os.host()。 比如,在 macOS 上默认编译平台是 macosx,如果当前项目是 ios 项目,那么可以设置默认编译平台为 iphoneos。 ```lua set_defaultplat("iphoneos") ``` 它等价于,`xmake f -p iphoneos`。 ## set\_defaultarchs ### 设置默认的编译架构 #### 函数原型 ::: tip API ```lua set_defaultarchs(archs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | archs | 默认编译架构字符串或数组,支持平台特定格式如 "iphoneos|arm64" | | ... | 可变参数,可传入多个架构规格 | #### 用法说明 v2.5.6 以上版本才支持,用于设置工程默认的编译架构,如果没有设置,默认平台跟随当前系统架构,也就是 os.arch()。 ```lua set_defaultplat("iphoneos") set_defaultarchs("arm64") ``` 它等价于,`xmake f -p iphoneos -a arm64`。 我们也可以设置多个平台下的默认架构。 ```lua set_defaultarchs("iphoneos|arm64", "windows|x64") ``` 在 iphoneos 上默认编译 arm64 架构,在 windows 上默认编译 x64 架构。 ## set\_defaultmode ### 设置默认的编译模式 #### 函数原型 ::: tip API ```lua set_defaultmode(mode: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | mode | 默认编译模式名称,如 "release", "debug", "releasedbg" | #### 用法说明 v2.5.6 以上版本才支持,用于设置工程默认的编译模式,如果没有设置,默认是 release 模式编译。 ```lua set_defaultmode("releasedbg") ``` 它等价于,`xmake f -m releasedbg`。 ## set\_allowedplats ### 设置允许编译的平台列表 #### 函数原型 ::: tip API ```lua set_allowedplats(platforms: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | platforms | 允许的编译平台名称字符串或数组 | | ... | 可变参数,可传入多个平台名称 | #### 用法说明 v2.5.6 以上版本才支持,用于设置工程支持的编译平台列表,如果用户指定了其他平台,会提示错误,这通常用于限制用户指定错误的无效平台。 如果没有设置,那么没有任何平台限制。 ```lua set_allowedplats("windows", "mingw") ``` 设置当前项目仅仅支持 windows 和 mingw 平台。 ## set\_allowedarchs ### 设置允许编译的平台架构 #### 函数原型 ::: tip API ```lua set_allowedarchs(archs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | archs | 允许的编译架构字符串或数组,支持平台特定格式如 "windows|x64" | | ... | 可变参数,可传入多个架构规格 | #### 用法说明 v2.5.6 以上版本才支持,用于设置工程支持的编译架构列表,如果用户指定了其他架构,会提示错误,这通常用于限制用户指定错误的无效架构。 如果没有设置,那么没有任何架构限制。 ```lua set_allowedarchs("x64", "x86") ``` 当前项目,仅仅支持 x64/x86 平台。 我们也可以同时指定多个平台下允许的架构列表。 ```lua set_allowedarchs("windows|x64", "iphoneos|arm64") ``` 设置当前项目在 windows 上仅仅支持 x64 架构,并且在 iphoneos 上仅仅支持 arm64 架构。 ## set\_allowedmodes ### 设置允许的编译模式列表 #### 函数原型 ::: tip API ```lua set_allowedmodes(modes: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | modes | 允许的编译模式名称字符串或数组 | | ... | 可变参数,可传入多个模式名称 | #### 用法说明 v2.5.6 以上版本才支持,用于设置工程支持的编译模式列表,如果用户指定了其他模式,会提示错误,这通常用于限制用户指定错误的无效模式。 如果没有设置,那么没有任何模式限制。 ```lua set_allowedmodes("release", "releasedbg") ``` 设置当前项目仅仅支持 release/releasedbg 两个编译模式。 ## namespace ### 进入命名空间 #### 函数原型 ::: tip API ```lua namespace(name: , script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 命名空间名称字符串 | | script | 命名空间脚本函数 | #### 用法说明 进入命名空间,xmake 2.9.8 版本支持,可以用于隔离子工程的重名 target,option 等各种域名冲突。 ### 隔离 target 对于命名空间内部的 target 访问,完全可以按现有的方式,不加任何命名空间,直接访问,而跨命名空间访问,则需要指定 `namespace::` 去指定。 ```lua add_rules("mode.debug", "mode.release") namespace("ns1", function () target("foo") set_kind("static") add_files("src/foo.cpp") namespace("ns2", function() target("bar") set_kind("static") add_files("src/bar.cpp") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") end) ``` 我们指定构建特定 target 时,也可以通过命名空间来定位。 ```sh $ xmake build -r ns1::test [ 33%]: cache compiling.release ns1::ns2::src/bar.cpp [ 41%]: cache compiling.release ns1::src/foo.cpp [ 50%]: cache compiling.release ns1::src/main.cpp [ 58%]: archiving.release ns1::ns2::libbar.a [ 75%]: archiving.release ns1::libfoo.a [ 91%]: linking.release ns1::test [100%]: build ok, spent 1.325s ``` 另外,命名空间也能隔离根域的配置,每个命名空间都有独立子根域,可以单独设置全局配置。 ```lua add_rules("mode.debug", "mode.release") add_defines("ROOT") namespace("ns1", function () add_defines("NS1_ROOT") target("foo") set_kind("static") add_files("src/foo.cpp") add_defines("FOO") namespace("ns2", function () add_defines("NS2_ROOT") target("bar") set_kind("static") add_files("src/bar.cpp") add_defines("BAR") end) end) target("test") set_kind("binary") add_deps("ns1::foo", "ns1::ns2::bar") add_files("src/main.cpp") add_defines("TEST") ``` 我们还可以隔离 includes 引入的子工程。 ```lua add_rules("mode.debug", "mode.release") add_defines("ROOT") namespace("ns1", function () add_defines("NS1_ROOT") target("foo") set_kind("static") add_files("src/foo.cpp") add_defines("FOO") includes("src") end) target("test") set_kind("binary") add_deps("ns1::foo", "ns1::ns2::bar") add_files("src/main.cpp") add_defines("TEST") ``` ### 隔离 option ```sh $ xmake f --opt0=y $ xmake f --ns1::opt1=y $ xmake f --ns1::ns2::opt2=y ``` ```lua add_rules("mode.debug", "mode.release") option("opt0", {default = true, defines = "OPT0", description = "option0"}) namespace("ns1", function () option("opt1", {default = true, defines = "NS1_OPT1", description = "option1"}) target("foo") set_kind("static") add_files("src/foo.cpp") add_options("opt1") namespace("ns2", function() option("opt2", {default = true, defines = "NS2_OPT2", description = "option2"}) target("bar") set_kind("static") add_files("src/bar.cpp") add_options("opt2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") add_options("opt0", "opt1", "ns2::opt2") end) ``` ### 隔离 rule ```lua add_rules("mode.debug", "mode.release") rule("rule0") on_load(function (target) target:add("defines", "RULE0") end) namespace("ns1", function () rule("rule1") on_load(function (target) target:add("defines", "NS1_RULE1") end) target("foo") set_kind("static") add_files("src/foo.cpp") add_rules("rule1") namespace("ns2", function() rule("rule2") on_load(function (target) target:add("defines", "NS2_RULE2") end) target("bar") set_kind("static") add_files("src/bar.cpp") add_rules("rule2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") add_rules("rule0", "rule1", "ns2::rule2") end) ``` ### 隔离 task ```sh xmake task0 xmake ns1::task1 xmake ns1::ns2::task2 ``` ```lua task("task0") set_menu {options = {}} on_run(function () print("task0") end) namespace("ns1", function () task("task1") set_menu {options = {}} on_run(function () print("NS1_TASK1") end) namespace("ns2", function() task("task2") set_menu {options = {}} on_run(function () print("NS2_TASK2") end) end) end) ``` ### 隔离 toolchain ```lua toolchain("toolchain0") on_load(function (toolchain) toolchain:add("defines", "TOOLCHAIN0") end) namespace("ns1", function () toolchain("toolchain1") on_load(function (toolchain) toolchain:add("defines", "NS1_TOOLCHAIN1") end) target("foo") set_kind("static") add_files("src/foo.cpp") set_toolchains("toolchain1") namespace("ns2", function() toolchain("toolchain2") on_load(function (toolchain) toolchain:add("defines", "NS2_TOOLCHAIN2") end) target("bar") set_kind("static") add_files("src/bar.cpp") set_toolchains("toolchain2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") set_toolchains("toolchain0", "toolchain1", "ns2::toolchain2") end) ``` ### 隔离 package ```lua add_requires("package0", {system = false}) package("package0") on_load(function (package) package:add("defines", "PACKAGE0") end) on_install(function (package) end) namespace("ns1", function () add_requires("package1", {system = false}) package("package1") on_load(function (package) package:add("defines", "NS1_PACKAGE1") end) on_install(function (package) end) target("foo") set_kind("static") add_files("src/foo.cpp") add_packages("package1") namespace("ns2", function() add_requires("package2", {system = false}) package("package2") on_load(function (package) package:add("defines", "NS2_PACKAGE2") end) on_install(function (package) end) target("bar") set_kind("static") add_files("src/bar.cpp") add_packages("package2") end) target("test") set_kind("binary") add_deps("foo", "ns2::bar") add_files("src/main.cpp") add_packages("package0", "package1", "ns2::package2") end) ``` ## namespace\_end ### 结束命名空间 #### 函数原型 ::: tip API ```lua namespace_end() ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | (无) | 无需参数 | #### 用法说明 结束当前的命名空间。 ```lua namespace("test") target("hello") add_files("src/*.c") namespace_end() ``` 除了使用 namespace\_end,我们也可以使用下面的语法,来结束命名空间,并且对 LSP 更加友好,具体使用哪种方式,根据用户自己的需求和喜好决定。 ```lua namespace("test", function () target("hello") add_files("src/*.c") end) ``` --- --- url: /zh/posts/next-plan.md --- 最近有很多用户反馈xmake在windows上编译体验不是很好,不方便进行调试和开发。。 其实xmake的定位主要还是以直接编译为主,提供跨平台的编译和部署,不依赖第三方IDE工程,不过目前确实在windows的体验还不是很好 尽管我已经优化了在windows下的编译速度,并且提供了`xmake run -d xxxx`方式,直接加载调试器进行源码调试 但是毕竟整体开发上,没有IDE的支持,对于习惯IDE开发的用户来讲,就不是那么友好了。(虽然我个人觉得用编辑器+printf的方式已经够用了) 因此我下一步计划(原本打算先做好包管理的),打算优先开始支持对Visual Stdio工程文件的生成,到时候会通过`project`插件的方式提供,例如: 创建vs2008工程文件: ```bash $ xmake project -k vs2008 ``` 创建vs2015工程文件,并且输出到`f:\vsproject`目录: ```bash $ xmake project -k vs2015 f:\vsproject ``` 创建makefile文件(这个已实现): ```bash $ xmake project -k makefile ``` xmake是可以灵活扩展插件的,如果有同学对xmake比较感兴趣,也想贡献插件的话(例如生成一些其他IDE工程文件。。),我还是非常欢迎的哈:) 对于插件的开发,可以参考我之前写的一些[文档](https://xmake.io/zh/) 不过,由于个人还有很多工作上的事,因此空闲时间不是很多,对vs工程插件的开发也许会陆陆续续进行,具体什么时候完成,暂时我也无法确定,请大家见谅。。 xmake的目标,不仅要做到跨平台构建和部署,还要提供最好的编译和开发体验,也许现在xmake还没有那么完善,体验上不是很好,但是大家可以多提意见,我会尽量去不断优化它。。 --- --- url: /zh/api/description/builtin-variables.md --- # 内建变量 {#builtin-variables} Xmake 提供了 `$(varname)` 的语法,来支持内置变量的获取,例如: ```lua add_cxflags("-I$(builddir)") ``` 它将会在在实际编译的时候,将内置的 `builddir` 变量转换为实际的构建输出目录:`-I./build` 一般内置变量可用于在传参时快速获取和拼接变量字符串,例如: ```lua target("test") -- 添加工程源码目录下的源文件 add_files("$(projectdir)/src/*.c") -- 添加构建目录下的头文件搜索路径 add_includedirs("$(builddir)/inc") ``` 也可以在自定义脚本的模块接口中使用,例如: ```lua target("test") on_run(function (target) -- 复制当前脚本目录下的头文件到输出目录 os.cp("$(scriptdir)/xxx.h", "$(builddir)/inc") end) ``` 这种使用内置变量的方式,使得描述编写更加的简洁易读。 当然这种变量模式,也是可以扩展的,默认通过`xmake f --var=val`命令,配置的参数都是可以直接获取,例如: ```lua target("test") add_defines("-DTEST=$(var)") ``` ::: tip NOTE 所有`xmake f --xxx=...`配置的参数值,都是可以通过内置变量获取到,例如:`xmake f --arch=x86`对应`$(arch)`,其他的还有`$(plat)`, `$(mode)`等等。 具体有哪些参数,可以通过:`xmake f -h`才查看。 ::: 既然支持直接从配置选项中获取,那么当然也就能很方便的扩展自定义的选项,来获取自定义的变量了,具体如何自定义选项见:[option](/zh/api/description/configuration-option) ## var.$(os) * 获取当前编译平台的操作系统 如果当前编译的是iphoneos,那么这个值就是:`ios`,以此类推。 ## var.$(host) * 获取本机操作系统 指的是当前本机环境的主机系统,如果你是在macOS上编译,那么系统就是:`macosx` ## var.$(tmpdir) * 获取临时目录 一般用于临时存放一些非永久性文件。 ## var.$(curdir) * 获取当前目录 一般默认是执行`xmake`命令时的工程根目录,当然如果通过[os.cd](/zh/api/scripts/builtin-modules/os#os-cd)改变了目录的话,这个值也会一起改变。 ## var.$(builddir) * 获取当前的构建输出目录 默认一般为当前工程根目录下的:`./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)") ``` --- --- url: /zh/guide/extensions/builtin-plugins.md --- # 内置插件 {#builtin-plugins} ## 生成 IDE 工程文件 {#generate-ide-projects} ### 简介 XMake 跟 `cmake`、`premake` 等其他一些构建工具的区别在于: ::: tip 注意 `xmake` 默认是直接构建运行的,生成第三方 IDE 的工程文件仅仅作为 `插件` 来提供。 ::: 这样做的一个好处是:插件更容易扩展,维护也更加独立和方便。 ### 生成 Makefile {#generate-makefile} ```sh $ xmake project -k makefile ``` ### 生成 CMakelists.txt {#generate-cmakelists} ```sh $ xmake project -k cmakelists ``` ### 生成 build.ninja {#generate-build-ninja} ```sh $ xmake project -k ninja ``` ### 生成 compiler\_flags {#generate-compiler-flags} ```sh $ xmake project -k compiler_flags ``` ### 生成 compile\_commands {#generate-compile-commands} 导出每个源文件的编译信息,生成基于 clang 的编译数据库文件,json 格式,可用于与 IDE、编辑器、静态分析工具进行交互。 ```sh $ 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) ### 生成 Xcode 工程文件 {#generate-xcode-project} 目前历史版本是利用 CMake 来生成的 Xcode 工程,不过最新的 dev 版本,也就是后续即将发布的 3.0.1 版本,将会带来原生的 Xcode 生成器。 如果想要提前体验,可以更新到 xmake dev 版本试用,`xmake update -s dev`。 具体详情见:[issue #4810](https://github.com/xmake-io/xmake/issues/4810)。 ```sh $ xmake project -k xcode ``` ### 生成 VisualStudio 工程 {#generate-vs-project} #### 使用 xmake 集成编译 {#generate-vsxmake} v2.2.8以上版本,提供了新版本的vs工程生成插件扩展,与之前的生成vs的插件处理模式有很大不同,之前生成的vs工程是把所有文件的编译展开后,转交给vs来处理编译。 但是这种模式,对xmake的rules是没法支持的。因为xmake的rules里面用了很多的`on_build`此类自定义脚本,无法展开,所以像qt, wdk此类的项目就没法支持导出到vs里面进行编译了。 因此,为了解决这个问题,新版本的vs生成插件通过在vs下直接调用xmake命令,去执行编译操作,并且对intellsence和定义跳转,还有断点调试也做了支持。 具体使用方式跟老版本类似: ```sh $ xmake project -k [vsxmake2010|vsxmake2013|vsxmake2015|..] -m "debug;release" ``` 如果没指明版本,那么xmake会自动探测当前已有的vs版本来生成: ```sh $ xmake project -k vsxmake -m "debug,release" ``` ![](/assets/img/manual/qt_vs.png) 另外,vsxmake插件还会额外生成一个自定义的配置属性页,用于在vs里面,方便灵活的修改和追加一些xmake编译配置,甚至可以在里面配置切换到其他交叉工具链,实现在vs中对android, linux等其他平台的交叉编译。 ![](/assets/img/manual/property_page_vsxmake.png) v2.5.1 版本提供了一个 `add_rules("plugin.vsxmake.autoupdate")` 规则,如果应用此规则,生成的vs工程在编译完成后,会检测 xmake.lua 和代码文件列表的改动,如果有变化,就会自动更新 vs 工程。 ```lua add_rules("plugin.vsxmake.autoupdate") target("test") set_kind("binary") add_files("src/*.c") ``` 另外,我们可以通过 `set_group` 接口对每个 target 设置分组,使得生成的 vs 工程可以按指定结构进行分组。更多详情见:[issue #1026](https://github.com/xmake-io/xmake/issues/1026) #### 使用 vs 内置编译机制 {#generate-vs} ::: tip 注意 建议尽量使用上文提到的v2.2.8之后提供的新版的vs生成插件,支持更加完善,此处的生成方式不支持xmake的rules,以及对qt等工程的生成。 ::: ```sh $ xmake project -k [vs2008|vs2013|vs2015|..] ``` v2.1.2以上版本,增强了vs201x版本工程的生成,支持多模式+多架构生成,生成的时候只需要指定: ```sh $ xmake project -k vs2017 -m "debug,release" ``` 生成后的工程文件,同时支持`debug|x86`, `debug|x64`, `release|x86`, `release|x64`四种配置模式。 如果不想每次生成的时候,指定模式,可以把模式配置加到`xmake.lua`的中,例如: ```lua -- 配置当前的工程,支持哪些编译模式 add_rules("mode.debug", "mode.release") ``` 另外,我们可以通过 `set_group` 接口对每个 target 设置分组,使得生成的 vs 工程可以按指定结构进行分组。更多详情见:[issue #1026](https://github.com/xmake-io/xmake/issues/1026) ## 运行自定义 lua 脚本 {#run-lua-scripts} 这个跟宏脚本类似,只是省去了导入导出操作,直接指定lua脚本来加载运行,这对于想要快速测试一些接口模块,验证自己的某些思路,都是一个不错的方式。 ### 运行指定的脚本文件 我们先写个简单的lua脚本: ```lua function main() print("hello xmake!") end ``` 然后直接运行它就行了: ```sh $ xmake lua /tmp/test.lua ``` ::: tip 注意 当然,你也可以像宏脚本那样,使用`import`接口导入扩展模块,实现复杂的功能。 ::: ### 运行内置的脚本命令 你可以运行 `xmake lua -l` 来列举所有内置的脚本名,例如: ```sh $ xmake lua -l scripts: cat cp echo versioninfo ... ``` 并且运行它们: ```sh $ xmake lua cat ~/file.txt $ xmake lua echo "hello xmake" $ xmake lua cp /tmp/file /tmp/file2 $ xmake lua versioninfo ``` ### 运行交互命令 (REPL) 有时候在交互模式下,运行命令更加方便测试和验证一些模块和 API,也更加灵活,不需要再去额外写一个脚本文件来加载。 我们先看下,如何进入交互模式: ```sh # 不带任何参数执行,就可以进入 $ xmake lua > # 进行表达式计算 > 1 + 2 3 # 赋值和打印变量值 > a = 1 > a 1 # 多行输入和执行 > for _, v in pairs({1, 2, 3}) do >> print(v) >> end 1 2 3 ``` 我们也能够通过 `import` 来导入扩展模块: ```sh > task = import("core.project.task") > task.run("hello") hello xmake! ``` 如果要中途取消多行输入,只需要输入字符:`q` 就行了 ```sh > for _, v in ipairs({1, 2}) do >> print(v) >> q <-- 取消多行输入,清空先前的输入数据 > 1 + 2 3 ``` ## 显示指定信息和列表 {#xmake-show} ### 显示xmake自身和当前项目的基础信息 ```sh $ xmake show The information of xmake: version: 2.3.3+202006011009 host: macosx/x86_64 programdir: /Users/ruki/.local/share/xmake programfile: /Users/ruki/.local/bin/xmake globaldir: /Users/ruki/.xmake tmpdir: /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/200603 workingdir: /Users/ruki/projects/personal/tbox packagedir: /Users/ruki/.xmake/packages packagedir(cache): /Users/ruki/.xmake/cache/packages/2006 The information of project: tbox version: 1.6.5 plat: macosx arch: x86_64 mode: release buildir: build configdir: /Users/ruki/projects/personal/tbox/.xmake/macosx/x86_64 projectdir: /Users/ruki/projects/personal/tbox projectfile: /Users/ruki/projects/personal/tbox/xmake.lua ``` ### 显示工具链列表 ```sh $ xmake show -l toolchains xcode Xcode IDE vs VisualStudio IDE yasm The Yasm Modular Assembler clang A C language family frontend for LLVM go Go Programming Language Compiler dlang D Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit ndk Android NDK rust Rust Programming Language Compiler llvm A collection of modular and reusable compiler and toolchain technologies cross Common cross compilation toolchain nasm NASM Assembler gcc GNU Compiler Collection mingw Minimalist GNU for Windows gnu-rm GNU Arm Embedded Toolchain envs Environment variables toolchain fasm Flat Assembler ``` ### 显示指定 target 配置信息 我们可以用它来快速追溯定位一些特定配置的位置。 ```sh $ xmake show -t tbox The information of target(tbox): at: /Users/ruki/projects/personal/tbox/src/tbox/xmake.lua kind: static targetfile: build/macosx/x86_64/release/libtbox.a rules: -> mode.release -> ./xmake.lua:26 -> mode.debug -> ./xmake.lua:26 -> mode.profile -> ./xmake.lua:26 -> mode.coverage -> ./xmake.lua:26 -> utils.install.cmake_importfiles -> ./src/tbox/xmake.lua:15 -> utils.install.pkgconfig_importfiles -> ./src/tbox/xmake.lua:16 options: -> info -> ./src/tbox/xmake.lua:50 -> float -> ./src/tbox/xmake.lua:50 -> wchar -> ./src/tbox/xmake.lua:50 -> exception -> ./src/tbox/xmake.lua:50 -> force-utf8 -> ./src/tbox/xmake.lua:50 -> deprecated -> ./src/tbox/xmake.lua:50 -> xml -> ./src/tbox/xmake.lua:53 -> zip -> ./src/tbox/xmake.lua:53 -> hash -> ./src/tbox/xmake.lua:53 -> regex -> ./src/tbox/xmake.lua:53 -> coroutine -> ./src/tbox/xmake.lua:53 -> object -> ./src/tbox/xmake.lua:53 -> charset -> ./src/tbox/xmake.lua:53 -> database -> ./src/tbox/xmake.lua:53 packages: -> mbedtls -> ./src/tbox/xmake.lua:43 -> polarssl -> ./src/tbox/xmake.lua:43 -> openssl -> ./src/tbox/xmake.lua:43 -> pcre2 -> ./src/tbox/xmake.lua:43 -> pcre -> ./src/tbox/xmake.lua:43 -> zlib -> ./src/tbox/xmake.lua:43 -> mysql -> ./src/tbox/xmake.lua:43 -> sqlite3 -> ./src/tbox/xmake.lua:43 links: -> pthread -> option(__keyword_thread_local) -> @programdir/includes/check_csnippets.lua:100 syslinks: -> pthread -> ./xmake.lua:71 -> dl -> ./xmake.lua:71 -> m -> ./xmake.lua:71 -> c -> ./xmake.lua:71 defines: -> __tb_small__ -> ./xmake.lua:42 -> __tb_prefix__="tbox" -> ./src/tbox/xmake.lua:19 -> _GNU_SOURCE=1 -> option(__systemv_semget) -> @programdir/includes/check_cfuncs.lua:104 cxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:22 -> -fno-strict-aliasing -> ./xmake.lua:22 -> -Wno-error=expansion-to-defined -> ./xmake.lua:22 -> -fno-stack-protector -> ./xmake.lua:51 frameworks: -> CoreFoundation -> ./src/tbox/xmake.lua:38 -> CoreServices -> ./src/tbox/xmake.lua:38 mxflags: -> -Wno-error=deprecated-declarations -> ./xmake.lua:23 -> -fno-strict-aliasing -> ./xmake.lua:23 -> -Wno-error=expansion-to-defined -> ./xmake.lua:23 includedirs: -> src -> ./src/tbox/xmake.lua:26 -> build/macosx/x86_64/release -> ./src/tbox/xmake.lua:27 headerfiles: -> src/(tbox/**.h)|**/impl/**.h -> ./src/tbox/xmake.lua:30 -> src/(tbox/prefix/**/prefix.S) -> ./src/tbox/xmake.lua:31 -> src/(tbox/math/impl/*.h) -> ./src/tbox/xmake.lua:32 -> src/(tbox/utils/impl/*.h) -> ./src/tbox/xmake.lua:33 -> build/macosx/x86_64/release/tbox.config.h -> ./src/tbox/xmake.lua:34 files: -> src/tbox/*.c -> ./src/tbox/xmake.lua:56 -> src/tbox/hash/bkdr.c -> ./src/tbox/xmake.lua:57 -> src/tbox/hash/fnv32.c -> ./src/tbox/xmake.lua:57 -> src/tbox/hash/adler32.c -> ./src/tbox/xmake.lua:57 -> src/tbox/math/**.c -> ./src/tbox/xmake.lua:58 -> src/tbox/libc/**.c|string/impl/**.c -> ./src/tbox/xmake.lua:59 -> src/tbox/utils/*.c|option.c -> ./src/tbox/xmake.lua:60 -> src/tbox/prefix/**.c -> ./src/tbox/xmake.lua:61 -> src/tbox/memory/**.c -> ./src/tbox/xmake.lua:62 -> src/tbox/string/**.c -> ./src/tbox/xmake.lua:63 -> src/tbox/stream/**.c|**/charset.c|**/zip.c -> ./src/tbox/xmake.lua:64 -> src/tbox/network/**.c|impl/ssl/*.c -> ./src/tbox/xmake.lua:65 -> src/tbox/algorithm/**.c -> ./src/tbox/xmake.lua:66 -> src/tbox/container/**.c|element/obj.c -> ./src/tbox/xmake.lua:67 -> src/tbox/libm/impl/libm.c -> ./src/tbox/xmake.lua:68 -> src/tbox/libm/idivi8.c -> ./src/tbox/xmake.lua:73 -> src/tbox/libm/ilog2i.c -> ./src/tbox/xmake.lua:70 -> src/tbox/libm/isqrti.c -> ./src/tbox/xmake.lua:71 -> src/tbox/libm/isqrti64.c -> ./src/tbox/xmake.lua:72 -> src/tbox/platform/*.c|context.c|exception.c -> ./src/tbox/xmake.lua:74 -> src/tbox/platform/impl/*.c|charset.c|poller_fwatcher.c -> ./src/tbox/xmake.lua:74 -> src/tbox/libm/*.c -> ./src/tbox/xmake.lua:77 compiler (cc): /usr/bin/xcrun -sdk macosx clang -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk linker (ar): /usr/bin/xcrun -sdk macosx ar -> -cr compflags (cc): -> -Qunused-arguments -target x86_64-apple-macos12.6 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.0.sdk -Wall -Werror -Oz -std=c99 -Isrc -Ibuild/macosx/x86_64/release -D__tb_small__ -D__tb_prefix__=\"tbox\" -D_GNU_SOURCE=1 -framework CoreFoundation -framework CoreServices -Wno-error=deprecated-declarations -fno-strict-aliasing -Wno-error=expansion-to-defined -fno-stack-protector linkflags (ar): -> -cr ``` ### 显示内置编译模式列表 ```sh $ xmake show -l buildmodes ``` ### 显示内置编译规则列表 ```sh $ xmake show -l rules ``` ### 显示其他信息 还在完善中,详情见:https://github.com/xmake-io/xmake/issues/798 或者运行: ```sh $ xmake show --help ``` ## 监视文件更新 {#xmake-watch} v2.7.1 版本新增了 `xmake watch` 插件命令,可以自动监视项目文件更新,然后触发自动构建,或者运行一些自定义命令。 这通常用于个人开发时候,实现快速的实时增量编译,而不需要每次手动执行编译命令,提高开发效率。 ### 项目更新后自动构建 默认行为就是监视整个项目根目录,任何文件改动都会触发项目的增量编译。 ```sh $ xmake watch watching /private/tmp/test/src/** .. watching /private/tmp/test/* .. /private/tmp/test/src/main.cpp modified [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! ``` ### 监视指定目录 我们也可以监视指定的代码目录,缩小监视范围,提升监视性能。 ```sh $ xmake watch -d src $ xmake watch -d "src;tests/*" ``` 上面的命令,会去递归监视所有子目录,如果想要紧紧监视当前目录下的文件,不进行递归监视,可以使用下面的命令。 ```sh $ xmake watch -p src $ xmake watch -p "src;tests/*" ``` ### 监视并运行指定命令 如果想在自动构建后,还想自动运行构建的程序,我们可以使用自定义的命令集。 ```sh $ xmake watch -c "xmake; xmake run" ``` 上面的命令列表是作为字符串传递,这对于复杂命令参数,需要转义比较繁琐不够灵活,那么我们可以使用下面的方式进行任意命令的设置。 ```sh $ xmake watch -- echo hello xmake! $ xmake watch -- xmake run --help ``` ### 监视并运行目标程序 尽管我们可以通过自定义命令来实现目标程序的自动运行,但是我们也提供了更加方便的参数来实现这个行为。 ```sh $ xmake watch -r $ xmake watch --run [100%]: build ok! hello world! ``` ### 监视并运行 lua 脚本 我们还可以监视文件更新后,运行指定的 lua 脚本,实现更加灵活复杂的命令定制。 ```sh $ xmake watch -s /tmp/test.lua ``` 我们还可以再脚本中获取所有更新的文件路径列表和事件。 ```lua function main(events) -- TODO handle events end ``` ## 分析诊断工程配置和代码 {#xmake-check} ### 检测工程配置 #### 默认检测所有 API ```lua set_lanuages("c91") -- typo ``` ```sh $ xmake check ./xmake.lua:15: warning: unknown language value 'c91', it may be 'c90' 0 notes, 1 warnings, 0 errors ``` 默认也可以指定检测特定组: ```sh $ xmake check api $ xmake check api.target ``` #### 显示详细输出 这会额外提供 note 级别的检测信息。 ```sh $ xmake check -v ./xmake.lua:15: warning: unknown language value 'cxx91', it may be 'cxx98' ./src/tbox/xmake.lua:43: note: unknown package value 'mbedtls' ./src/tbox/xmake.lua:43: note: unknown package value 'polarssl' ./src/tbox/xmake.lua:43: note: unknown package value 'openssl' ./src/tbox/xmake.lua:43: note: unknown package value 'pcre2' ./src/tbox/xmake.lua:43: note: unknown package value 'pcre' ./src/tbox/xmake.lua:43: note: unknown package value 'zlib' ./src/tbox/xmake.lua:43: note: unknown package value 'mysql' ./src/tbox/xmake.lua:43: note: unknown package value 'sqlite3' 8 notes, 1 warnings, 0 errors ``` #### 检测指定的 API ```sh $ xmake check api.target.languages ./xmake.lua:15: warning: unknown language value 'cxx91', it may be 'cxx98' 0 notes, 1 warnings, 0 errors ``` #### 检测编译 flags ```sh $ xmake check ./xmake.lua:10: warning: clang: unknown c compiler flag '-Ox' 0 notes, 1 warnings, 0 errors ``` #### 检测 includedirs 除了 includedirs,还有 linkdirs 等路径都会去检测。 ```sh $ xmake check ./xmake.lua:11: warning: includedir 'xxx' not found 0 notes, 1 warnings, 0 errors ``` ### 检测工程代码(clang-tidy) #### 显示 clang-tidy 检测列表 ```sh $ xmake check clang.tidy --list Enabled checks: clang-analyzer-apiModeling.StdCLibraryFunctions clang-analyzer-apiModeling.TrustNonnull clang-analyzer-apiModeling.google.GTest clang-analyzer-apiModeling.llvm.CastValue clang-analyzer-apiModeling.llvm.ReturnValue ... ``` #### 检测所有 targets 中的源码 ```sh $ xmake check clang.tidy 1 error generated. Error while processing /private/tmp/test2/src/main.cpp. /tmp/test2/src/main.cpp:1:10: error: 'iostr' file not found [clang-diagnostic-error] #include ^~~~~~~ Found compiler error(s). error: execv(/usr/local/opt/llvm/bin/clang-tidy -p compile_commands.json /private/tmp/test2/src /main.cpp) failed(1) ``` #### 指定检测类型 我们可以在 `--check=` 中指定需要检测的类型,具体用法可以参考 `clang-tidy` 的 `--check=` 参数,完全一致的。 ```sh $ xmake check clang.tidy --checks="*" 6 warnings and 1 error generated. Error while processing /private/tmp/test2/src/main.cpp. /tmp/test2/src/main.cpp:1:10: error: 'iostr' file not found [clang-diagnostic-error] #include ^~~~~~~ /tmp/test2/src/main.cpp:3:1: warning: do not use namespace using-directives; use using-declarat ions instead [google-build-using-namespace] using namespace std; ^ /tmp/test2/src/main.cpp:3:17: warning: declaration must be declared within the '__llvm_libc' na mespace [llvmlibc-implementation-in-namespace] using namespace std; ^ /tmp/test2/src/main.cpp:5:5: warning: declaration must be declared within the '__llvm_libc' nam espace [llvmlibc-implementation-in-namespace] int main(int argc, char **argv) { ^ /tmp/test2/src/main.cpp:5:5: warning: use a trailing return type for this function [modernize-u se-trailing-return-type] int main(int argc, char **argv) { ~~~ ^ auto -> int /tmp/test2/src/main.cpp:5:14: warning: parameter 'argc' is unused [misc-unused-parameters] int main(int argc, char **argv) { ^~~~ /*argc*/ /tmp/test2/src/main.cpp:5:27: warning: parameter 'argv' is unused [misc-unused-parameters] int main(int argc, char **argv) { ^~~~ /*argv*/ Found compiler error(s). error: execv(/usr/local/opt/llvm/bin/clang-tidy --checks=* -p compile_commands.json /private/tm p/test2/src/main.cpp) failed(1) ``` #### 检测指定 target 的代码 ```sh $ xmake check clang.tidy [targetname] ``` #### 检测给定的源文件列表 ```sh $ xmake check clang.tidy -f src/main.c $ xmake check clang.tidy -f 'src/*.c:src/**.cpp' ``` #### 设置 .clang-tidy 配置文件 ```sh $ xmake check clang.tidy --configfile=/tmp/.clang-tidy ``` #### 创建 .clang-tidy 配置文件 ```sh $ xmake check clang.tidy --checks="*" --create $ cat .clang-tidy --- Checks: 'clang-diagnostic-*,clang-analyzer-*,*' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false FormatStyle: none User: ruki CheckOptions: - key: readability-suspicious-call-argument.PrefixSimilarAbove value: '30' - key: cppcoreguidelines-no-malloc.Reallocations value: '::realloc' ``` #### 自动修复错误代码 我们可以使用下面的命令参数,自动修复 clang tidy 检测出来的问题代码。 ```console $ xmake check clang.tidy --fix $ xmake check clang.tidy --fix_errors $ xmake check clang.tidy --fix_notes ``` ## 生成安装包 (XPack) {#xpack} ### 简介 这个插件可以帮助用户快速生成不同平台的安装包,源码包,它会生成下面一些安装包格式: * Windows NSIS 二进制安装包 * Windows WIX 二进制安装包 * runself (shell) 自编译安装包 * zip/tar.gz 二进制包 * zip/tar.gz 源码包 * RPM 二进制安装包 * SRPM 源码安装包 * DEB 二进制安装包 下面是一个完整例子,我们可以先简单看下: ```lua set_version("1.0.0") add_rules("mode.debug", "mode.release") includes("@builtin/xpack") target("test") set_kind("binary") add_files("src/*.cpp") xpack("test") set_formats("nsis", "zip", "targz", "runself") set_title("hello") set_author("ruki") set_description("A test installer.") set_homepage("https://xmake.io") set_licensefile("LICENSE.md") add_targets("test") add_installfiles("src/(assets/*.png)", {prefixdir = "images"}) add_sourcefiles("(src/**)") set_iconfile("src/assets/xmake.ico") after_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) after_uninstallcmd(function (package, batchcmds) batchcmds:rmdir(package:installdir("resources")) batchcmds:rmdir(package:installdir("stub")) end) ``` 我们通过 `includes("@builtin/xpack")` 引入 xpack 的所有配置接口,包括 xpack 配置域,以及它的所有域接口。 然后我们执行: ```sh $ xmake pack ``` 即可生成所有安装包。 ### 生成 NSIS 安装包 {#xpack-nsis} 只要配置了 `set_formats("nsis")` 格式,然后执行 `xmake pack` 命令,就能生成 NSIS 格式的安装包。 另外,xmake 还会自动安装生成 NSIS 包所需的工具,实现真正的一键打包。 ```sh $ xmake pack note: install or modify (m) these packages (pass -y to skip confirm)? in xmake-repo: -> nsis 3.09 please input: y (y/n/m) => install nsis 3.09 .. ok [ 25%]: compiling.release src\main.cpp [ 37%]: compiling.release src\main.cpp [ 50%]: linking.release foo.dll [ 62%]: linking.release test.exe packing build\xpack\test\test-windows-x64-v1.0.0.exe pack ok ``` `test-windows-x64-v1.0.0.exe` 就是我们生成的安装包,双击运行它,就能安装我们的二进制文件到指定目录。 ![](/assets/img/manual/nsis_1.png) ![](/assets/img/manual/nsis_2.png) ![](/assets/img/manual/nsis_3.png) #### 增加组件安装 我们还可以给 NSIS 增加组件安装命令,只有当用户选择指定组件的时候,它的安装命令才会被执行。 ```lua xpack("test") add_components("LongPath") xpack_component("LongPath") set_default(false) set_title("Enable Long Path") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") on_installcmd(function (component, batchcmds) batchcmds:rawcmd("nsis", [[ ${If} $NoAdmin == "false" ; Enable long path WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ${EndIf}]]) end) ``` 这个例子中,我们在里面添加了一个 NSIS 特有的自定义命令,去实现对长路径的支持。 ![](/assets/img/manual/nsis_4.png) ### 生成自安装包 {#xpack-runself} 我们也可以生成基于 shell 脚本的自编译安装包。我们需要配置 runself 打包格式,然后通过 `add_sourcefiles` 添加需要参与编译安装的源文件。 接着,我们需要自定义 on\_installcmd 安装脚本,里面去配置如果编译源码包,我们可以简单的调用一个内置的编译安装脚本文件,也可以直接配置 `make install` 等编译安装命令。 例如: ```lua xpack("test") set_formats("runself") add_sourcefiles("(src/**)") on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install"}) end) ``` 然后,我们执行 `xmake pack` 命令,就可以生成一个自安装的 xxx.gz.run 包,默认采用 gzip 压缩。 ```sh $ xmake pack packing build/xpack/test/test-macosx-src-v1.0.0.gz.run pack ok ``` 我们可以使用 sh 去加载运行它来安装我们的程序。 ```sh $ sh ./build/xpack/test/test-macosx-src-v1.0.0.gz.run ``` 我们也可以看一个比较完整的例子: ```lua xpack("xmakesrc") set_formats("runself") set_basename("xmake-v$(version)") set_prefixdir("xmake-$(version)") before_package(function (package) import("devel.git") local rootdir = path.join(os.tmpfile(package:basename()) .. ".dir", "repo") if not os.isdir(rootdir) then os.tryrm(rootdir) os.cp(path.directory(os.projectdir()), rootdir) git.clean({repodir = rootdir, force = true, all = true}) git.reset({repodir = rootdir, hard = true}) if os.isfile(path.join(rootdir, ".gitmodules")) then git.submodule.clean({repodir = rootdir, force = true, all = true}) git.submodule.reset({repodir = rootdir, hard = true}) end end local extraconf = {rootdir = rootdir} package:add("sourcefiles", path.join(rootdir, "core/**|src/pdcurses/**|src/luajit/**|src/tbox/tbox/src/demo/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "xmake/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "*.md"), extraconf) package:add("sourcefiles", path.join(rootdir, "configure"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/*.sh"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/man/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/debian/**"), extraconf) package:add("sourcefiles", path.join(rootdir, "scripts/msys/**"), extraconf) end) on_installcmd(function (package, batchcmds) batchcmds:runv("./scripts/get.sh", {"__local__"}) end) ``` 它是 xmake 自身源码的安装包配置脚本,更完整的配置可以参考:[xpack.lua](https://github.com/xmake-io/xmake/blob/master/core/xpack.lua) 这里,它通过调用源码包内置的 `./scripts/get.sh` 安装脚本去执行编译安装。 ### 生成源码归档包 {#xpack-archive-source} 另外,我们也可以配置 `srczip` 和 `srctargz` 格式,来生成源码压缩包,它不是完整的安装包,也没有安装命令,仅仅用于源码包分发。 ```lua xpack("test") set_formats("srczip", "srctargz") add_sourcefiles("(src/**)") ``` ```sh $ xmake pack packing build/xpack/test/test-macosx-src-v1.0.0.zip .. packing build/xpack/test/test-macosx-src-v1.0.0.tar.gz .. pack ok ``` ### 生成二进制归档包 {#xpack-archive-binary} 我们也可以配置 `zip` 和 `targz` 来生成二进制的压缩包,它会先自动编译所有绑定的 target 目标程序,将所有需要的二进制程序,库文件打包到 zip/tar.gz 格式。 这通常用于制作绿色版的安装包,内部不太任何自动安装脚本,用户需要自己设置 PATH 等环境变量。 ```lua xpack("test") set_formats("zip", "targz") add_installfiles("(src/**)") ``` ```sh $ xmake pack packing build/xpack/test/test-macosx-v1.0.0.zip .. packing build/xpack/test/test-macosx-v1.0.0.tar.gz .. pack ok ``` ::: tip 注意 需要注意的是,打二进制文件到包里,使用的是 `add_installfiles` 而不是 `add_sourcefiles`。 ::: 我们也可以通过 `add_targets` 去绑定需要安装的 target 目标程序和库。更多详情见下面关于 `add_targets` 的接口描述。 ### 生成 SRPM 源码安装包 {#xpack-srpm} 它可以生成 `.src.rpm` 格式的源码安装包。 我们可以通过配置 add\_targets 关联需要构建的目标,在生成的 srpm 包中,它会自动调用 `xmake build` 和 `xmake install` 去构建和安装包。 ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./xmake.lua") add_targets("demo") ``` 它会生成类似下面的 spec 文件,然后自动调用 rpmbuild 去生成 `.src.rpm` 包。 ``` Name: test Version: 1.0.0 Release: 1%{?dist} Summary: hello License: Apache-2.0 URL: https://xmake.io Source0: test-linux-src-v1.0.0.tar.gz BuildRequires: xmake BuildRequires: gcc BuildRequires: gcc-c++ %description A test installer. %prep %autosetup -n test-1.0.0 -p1 %build xmake build -y test %install xmake install -o %{buildroot}/%{_exec_prefix} test cd %{buildroot} find . -type f | sed 's!^\./!/!' > %{_builddir}/_installedfiles.txt %check %files -f %{_builddir}/_installedfiles.txt %changelog * Fri Dec 22 2023 ruki - 1.0.0-1 - Update to 1.0.0 ``` 我们也可以通过 `on_buildcmd` 和 `on_installcmd` 自定义构建和安装脚本。 ```lua xpack("test") set_homepage("https://xmake.io") set_license("Apache-2.0") set_description("A cross-platform build utility based on Lua.") set_formats("srpm") add_sourcefiles("(src/**)") add_sourcefiles("./configure") on_buildcmd(function (package, batchcmds) batchcmds:runv("./configure") batchcmds:runv("make") end) on_installcmd(function (package, batchcmds) batchcmds:runv("make", {"install", "PREFIX=%{buildroot}"}) end) ``` ### 生成 RPM 二进制安装包 {#xpack-rpm} RPM 包将会直接生成编译好的二进制安装包。xmake 会自动调用 `rpmbuild --rebuild` 命令去构建 SRPM 包生成它。 而在 XPack 中,我们仅仅只需要配置 `set_formats("rpm")` 即可支持 rpm 包生成,其他配置与 srpm 包完全一致。 ```lua xpack("test") set_formats("rpm") -- TODO ``` ### 打包命令参数 #### 指定打包格式 如果我们在配置文件中已经使用 `set_formats` 配置了多个打包格式,那么默认情况下,`xmake pack` 会自动生成所有这些格式的包。 当然,我们也可以通过 `xmake pack --formats=nsis,targz` 来选择性指定当前需要打哪些格式的包。 #### 修改打包文件名 我们可以在配置文件中,通过 `set_basename()` 来修改包名,也可以通过命令行去修改它。 ```sh $ xmake pack --basename="foo" packing build/xpack/test/foo.zip .. pack ok ``` #### 指定输出目录 默认的输出目录是在 build 目录下,但我们也可以修改输出的路径。 ```sh $ xmake pack -o /tmp/output ``` #### 禁用自动构建 如果是打 NSIS 等二进制包,`xmake pack` 会先自动编译所有被绑定的 target 目标文件,然后再去执行打包逻辑。 但是如果我们已经编译过了,不想每次都去编译它,而是直接去打包,可以通过下面的参数禁用自动构建。 ```sh $ xmake pack --autobuild=n ``` ### 接口描述 更多 XPack 打包接口描述见:[XPack 打包接口文档](/zh/api/description/xpack-interfaces)。 ## 宏记录和回放 {#xmake-macro} ### 简介 我们可以通过这个插件,快速记录和回放我们平常频繁使用到的一些xmake操作,来简化我们日常的开发工作。 它提供了一些功能: * 手动记录和回放多条执行过的xmake命令 * 支持快速的匿名宏创建和回放 * 支持命名宏的长久记录和重用 * 支持宏脚本的批量导入和导出 * 支持宏脚本的删除、显示等管理功能 * 支持自定义高级宏脚本,以及参数配置 ### 记录操作 ```sh # 开始记录宏 $ xmake macro --begin # 执行一些xmake命令 $ xmake f -p android --ndk=/xxx/ndk -a arm64-v8a $ 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 ``` ### 回放 ```sh # 回放一个匿名宏 $ xmake macro . ``` ### 命名宏 匿名宏的好处就是快速记录,快速回放,如果需要长久保存,就需要给宏取个名字。 ```sh $ xmake macro --begin $ ... $ xmake macro --end macroname $ xmake macro macroname ``` ### 导入导出宏 导入指定的宏脚本或者宏目录: ```sh $ xmake macro --import=/xxx/macro.lua macroname $ xmake macro --import=/xxx/macrodir ``` 导出指定的宏到脚本或者目录: ```sh $ xmake macro --export=/xxx/macro.lua macroname $ xmake macro --export=/xxx/macrodir ``` ### 列举显示宏 列举所有`xmake`内置的宏脚本: ```sh $ xmake macro --list ``` 显示指定的宏脚本内容: ```sh $ xmake macro --show macroname ``` ### 自定义宏脚本 我们也可以自己编写个宏脚本 `macro.lua` 然后导入到xmake中去。 ```lua function main() os.exec("xmake f -p android --ndk=/xxx/ndk -a arm64-v8a") 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,并且定义宏名字: ```sh $ xmake macro --import=/xxx/macro.lua [macroname] ``` 回放这个宏脚本: ```sh $ xmake macro [.|macroname] ``` ### 内置的宏脚本 {#builtin-macro-scripts} XMake 提供了一些内置的宏脚本,来简化我们的日常开发工作。 例如,我们可以使用 `package` 宏来对`iphoneos`平台的所有架构,一次性批量构建和打包: ```sh $ 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 "", (option.get("verbose") and "-v" or "")) -- package it if args.outputdir then os.exec("xmake p -o %s %s", args.outputdir, (option.get("verbose") and "-v" or "")) else os.exec("xmake p %s", (option.get("verbose") and "-v" or "")) 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 ``` ::: tip 注意 如果你想要获取更多宏参数选项信息,请运行: `xmake macro --help` ::: ## 生成 doxygen 文档 {#xmake-doxygen} 请先确保本机已安装`doxygen`工具,然后在工程目录下运行: ```sh $ xmake doxygen ``` --- --- url: /zh/api/description/builtin-policies.md --- # 内置策略 {#builtin-policies} Xmake 有很多的默认行为,比如:自动检测和映射编译、链接标志、跨工程目标并行构建等,虽然提供了一定的智能化处理,但重口难调,不一定能满足所有的用户的使用习惯和需求。 因此, xmake 提供了**针对默认策略的修改设置**,在一定程度上给予了用户**修改策略**的权限。这个功能主要通过 [set\_policy](/zh/api/description/project-target#set-policy) 接口来实现,我们通常可以使用这个接口来配置修改 target, package 以及工程整体的一些行为策略。 ## 使用方式 {#usage} ::: tip 注意 如果设置的策略名是无效的, xmake 会有警告提示。 ::: ### 1. 获取当前版本所支持的所有策略 {#\_1-get-all-the-policies-supported-by-the-current-version} 执行下面的命令可以返回所有的配置及其描述、值类型和默认值: ```sh $ xmake l core.project.policy.policies ``` ### 2. 在 `xmake.lua` 中直接调用接口进行策略的配置 {#\_2-configuring-policies-in-xmake-lua} ::: code-group ```lua [全局设置] -- 在项目根域设置这个配置,就可以全局禁用 flags 的自动检测和忽略机制 set_policy("check.auto_ignore_flags", false) ``` ```lua [局部设置] target("test") -- 针对特定的 target `test` 生效 set_policy("check.auto_ignore_flags", false) ``` ::: ### 3. 通过命令行进行策略的配置 {#\_3-configuring-policies-via-the-command-line} 当我们构建工程时,可能需要暂时性地启用或是禁用一些策略。在这种情况下,使用命令行就更加贴合需求了。使用命令行设置策略时,默认该策略为启用状态: ```sh $ xmake f --policies=package.fetch_only ``` 当然,我们也可以指定其他值以达到禁用等效果: ```sh $ xmake f --policies=package.precompiled:n ``` 值得注意的是,同时配置多个策略需要用逗号进行分割: ```sh $ xmake f --policies=package.precompiled:n,package.install_only ``` ## check.auto\_ignore\_flags ::: danger 潜在风险 强行禁用自动检测和忽略标志的默认策略可能会导致其他用户在编译时由于编译器的支持力度不同,从而出现一定程度的编译失败。 ::: ### 默认策略 {#default-policy} xmake 默认会对所有通过 `add_cxflags`, `add_ldflags` 等接口添加的原始编译和链接标志进行自动检测。如果检测出当前编译器和链接器不支持某些标志,就会自动忽略并弹出警告信息。 这种自动忽略的策略通常是很有用的,尤其是在处理一些可选的编译标志时,即使编译器不支持也能正常编译。 ### 潜在需求 {#use-cases} 由于自动检测在某些情况下会有一定程度的误判(尤其是针对交叉编译工具链,更容易出现检测失败,导致编译失败),而且自动忽略的同时会产生警告提示,所以某些用户并不喜欢这个设定。 例如,如果标志检测失败,就会有警告提示用户: ```sh warning: add_ldflags("-static") is ignored, please pass `{force = true}` or call `set_policy("check.auto_ignore_flags", false)` if you want to set it. ``` 根据警告提示,我们可以自己分析判断是否需要强制添加这个标志。我们可以根据 flag 的数量来选择以下两种方案: #### 1. 添加 force 参数 {#\_1-add-the-force-parameter} ```lua add_ldflags("-static", {force = true}) ``` `{force = true}` 参数可以显式地强制添加标志并跳过自动检测,这对于**个别**的检测失败是**有效快捷**的处理方式,但是对于交叉编译,**大量**的标志检测不通过的情况下,每个都设置 force 参数过于**繁琐**。 #### 2. 配置策略 {#\_2-set-the-policy} 针对方案一中的局限性,我们可以使用 `set_policy` 直接禁用对某个目标或者整个项目默认的标志自动检测和忽略行为,从而达到没有额外警告信息出现的效果: ```lua set_policy("check.auto_ignore_flags", false) target("test") add_ldflags("-static") ``` ## check.auto\_map\_flags ::: tip 注意 目前的自动映射实现还不是很完整,没有 100% 覆盖所有 gcc 的标志,所以还是有部分标志是无法去映射的。 ::: ### 默认策略 {#default-policy-1} 这是 xmake 的另外一个对标志的智能分析处理。通常情况下,通过 `add_links`, `add_defines` 这种 xmake 内置的 API 去设置的配置是具有跨平台特性的。针对不同的编译器平台, xmake 会自动处理成对应的原始标志。 但是有些情况下,用户还是会通过 `add_cxflags`, `add_ldflags` 等接口设置原始的编译链接标志。这些 API 接收原始标志为参数,但部分标志本身并不是被所有的编译器支持的。 例如 `-O0` 这个编译优化标志。虽然有 `set_optimize` 来实现跨编译器配置,但如果用户可能会直接使用 `add_cxflags("-O0")` 。这种情况下, gcc/clang 下可以正常处理,但是 msvc 下就不支持它了。 因此, xmake 内置了标志的自动映射功能:基于 gcc 编译标志的普及性, xmake 采用 gcc 的标志命名规范,针对不同的编译器对其进行自动映射,例如: ```lua add_cxflags("-O0") ``` 这一行设置,在 gcc/clang 编译器下还是 `-O0` ,但针对 msvc 编译器, xmake 就会自动将其映射为 msvc 中对应的 `-Od` 编译标志来禁用优化。整个过程,用户是完全无感知的,直接执行 xmake 就可以跨编译器完成标志的映射和编译。 ### 用法 {#usage-1} 也有部分用户并不喜欢这种自动映射行为,那么我们可以通过下面的设置完全禁用这个默认的行为: ```lua set_policy("check.auto_map_flags", false) ``` ## check.target\_package\_licenses ### 默认策略 {#default-policy-2} 在软件开发过程中引入开源软件作为第三方依赖,可以有效避免“重造轮子”。我们应当**尊重并认可**其他开发者的劳动成果,**遵守**相应的开源许可证。为了帮助开发者发现潜在的**许可证冲突等风险**, xmake 默认会对项目和引入依赖的许可证进行**兼容性检查**。 ### 用法 {#usage-2} 这个策略主要用于辅助用户发现潜在的许可证合规问题,起一个提醒的作用。在某些学习或特定场景下,如果用户希望暂时屏蔽检测产生的冲突警告,可以通过以下设置禁用许可证兼容性检测并去除警告信息: ```lua set_policy("check.target_package_licenses", false) ``` ## build.across\_targets\_in\_parallel 这个策略也是默认开启的,主要用于跨target间执行并行构建,v2.3.3之前的版本,并行构建只能针对单个target内部的所有源文件, 跨target的编译,必须要要等先前的target完全link成功,才能执行下一个target的编译,这在一定程度上会影响编译速度。 然而每个target的源文件是可以完全并行化处理的,最终在一起执行link过程,v2.3.3之后的版本通过这个优化,构建速度提升了30%。 当然,如果有些特殊的target里面的构建源文件要依赖先前的target(尤其是一些自定义rules的情况,虽然很少遇到),我们也可以通过下面的设置禁用这个优化行为: ```sh set_policy("build.across_targets_in_parallel", false) ``` ## build.fence 由于配置 `set_policy("build.across_targets_in_parallel", false)` 存在局限性,它会限制父 target 和它的所有依赖的子 target 之间的并行度,影响的范围有点大。 而我们做 codegen 时候,有时候仅仅只是想对其中某个依赖的 target 限制并行度,作为 codegen 程序,提前让它完成编译。 这个时候,`build.across_targets_in_parallel` 就无法精细控制了,编译速度也无法达到最优。 因此,我们新增了 `build.fence` 策略,它可以仅仅只针对特定的子 target 限制并行编译链接。 相关的背景细节,可以看下:[#5003](https://github.com/xmake-io/xmake/issues/5003) 例如: ```lua target("autogen") set_default(false) set_kind("binary") set_plat(os.host()) set_arch(os.arch()) add_files("src/autogen.cpp") set_languages("c++11") set_policy("build.fence", true) target("test") set_kind("binary") add_deps("autogen") add_rules("autogen") add_files("src/main.cpp") add_files("src/*.in") ``` 其中 autogen 目标程序需要在 test 程序的源码被编译前,就要完成编译链接,因为 test 目标需要运行 autogen 程序,去动态生成一些源码参与编译。 而针对 autogen 配置 `set_policy("build.fence", true)` 就可以实现这个目的。 ## build.merge\_archive 如果设置了这个策略,那么使用 `add_deps()` 依赖的目标库不再作为链接存在,而是直接把它们合并到父目标库中去。 例如: ```lua add_rules("mode.debug", "mode.release") target("add") set_kind("static") add_files("src/add.c") add_files("src/subdir/add.c") target("sub") set_kind("static") add_files("src/sub.c") add_files("src/subdir/sub.c") target("mul") set_kind("static") add_deps("add", "sub") add_files("src/mul.c") set_policy("build.merge_archive", true) target("test") add_deps("mul") add_files("src/main.c") ``` libmul.a 静态库会自动合并 libadd.a 和 libsub.a 两个子依赖的静态库。 ## build.ccache Xmake 默认是开启内置的编译缓存的,通过设置这个策略,可以显式禁用缓存。 ```lua set_policy("build.ccache", false) ``` 当然,我们也可以命令行去禁用它。 ```sh $ xmake f --ccache=n ``` 或者 ```sh $ xmake f --policies=build.ccache:n ``` ## build.warning 默认编译通常不会实时回显警告输出,我们通常需要使用 `xmake -w` 开启,或者通过 `xmake g --build_warning=y` 来全局开启它。 现在,我们也可以在 xmake.lua 配置中去默认启用警告回显输出。 ```lua set_policy("build.warning", true) set_warnings("all", "extra") ``` 这个时候,即使我们执行 `xmake` 命令,也能直接回显警告输出。 ## build.optimization.lto 2.6.9 版本 xmake 改进了对 LTO 链接时优化的支持,对 gcc/clang/msvc 等不同平台下都进行了适配,只需要启用这个策略,就能对特定 target 开启 LTO。 ```lua set_policy("build.optimization.lto", true) ``` 我们也可以通过命令行选项快速开启。 ```sh $ xmake f --policies=build.optimization.lto ``` ## build.cuda.devlink 2.7.7 版本可以通过这个配置,显示开启对特定目标的设备链接。 这通常用于 Cuda 项目的构建,以及非 Cuda binary/shared 依赖 Cuda static 目标的情况,这个时候,Cuda static 目标就需要显示配置这个,开启设备链接。 ```lua target("test") set_kind("static") set_policy("build.cuda.devlink", true) ``` 而默认 Cuda binary/shared 是开启 devlink 的,我们也可以通过策略显示禁用它。 关于这个的详细背景说明,见:[#1976](https://github.com/xmake-io/xmake/issues/1976) ## build.distcc.remote\_only 这个策略用于配置分布式编译的行为。默认情况下,在启用分布式编译后,本机既充当调度节点,也会参与实际的编译工作。 如果启用这个策略,则强制仅让远程机器执行分布式编译任务,本机不参与实际编译,仅仅作为调度节点使用。这在本机资源有限,或者希望将所有编译工作都分配到远程服务器集群时非常有用。 ```lua set_policy("build.distcc.remote_only", true) ``` 或者通过命令行启用: ```sh $ xmake f --policies=build.distcc.remote_only ``` 关于分布式编译的更多详细信息,请参阅:[分布式编译](/zh/guide/extras/distributed-compilation) ## build.sanitizer.address Address Sanitizer(ASan)是一个快速的内存错误检测工具,由编译器内置支持,通常我们需要在编译和链接的 flags 中同时配置 `-fsanitize-address` 才能正确开启。 而我们可以通过开启这个策略,就可以快速全局启用它,这会使得编译出来的程序,直接支持 ASan 检测。 例如,我们可以通过命令行的方式去启用: ```sh $ xmake f --policies=build.sanitizer.address ``` 也可以通过接口配置去全局启用: ```lua set_policy("build.sanitizer.address", true) ``` 当然,我们也可以单独对某个特定的 target 去配置开启。 另外,如果全局配置它,我们就可以同时对所有依赖包也生效。 ```lua set_policy("build.sanitizer.address", true) add_requires("zlib") add_requires("libpng") ``` 它等价于,对每个包依次设置 asan 配置。 ```lua add_requires("zlib", {configs = {asan = true}}) add_requires("libpng", {configs = {asan = true}}) ``` ::: tip 注意 `add_rules("mode.asan", "mode.tsan", "mode.ubsan", "mode.msan")` 将被废弃,尽可能使用这些新的策略,因为这些构建模式无法同步对依赖包生效。 ::: 另外,我们也可以同时生效多个 sanitizer 检测,例如: ```lua set_policy("build.sanitizer.address", true) set_policy("build.sanitizer.undefined", true) ``` 或者 ``` $ xmake f --policies=build.sanitizer.address,build.sanitizer.undefined ``` ## build.sanitizer.thread 与 [build.sanitizer.address](#build-sanitizer-address) 类似,用于检测线程安全问题。 ## build.sanitizer.memory 与 [build.sanitizer.address](#build-sanitizer-address) 类似,用于检测内存问题。 ## build.sanitizer.leak 与 [build.sanitizer.address](#build-sanitizer-address) 类似,用于检测内存泄漏问题。 ## build.sanitizer.undefined 与 [build.sanitizer.address](#build-sanitizer-address) 类似,用于检测 undefined 问题。 ## build.always\_update\_configfiles 这个策略用于对 `add_configfiles` 配置文件的自动生成行为。默认情况下,xmake 仅仅只会在首次 `xmake config` 时候,或者 xmake.lua 配置有改动的是否,才会触发 configfiles 的重新生成。 之后的每次构建,只要配置没有变化,就不会重新生成 configfiles。 但是,如果我们的 configfiles 中有使用 GIT\_COMMIT 等变量,想要每次构建时候,总是重新生成最新的配置,那么可以配置它。 具体使用背景,可以看下:[#4747](https://github.com/xmake-io/xmake/issues/4747) ## build.intermediate\_directory 配置启用或禁用构建的内部子目录。 默认情况下,执行 `xmake` 编译项目会自动在 build 目录下根据平台。架构,编译模式生成子目录,分别存储对象文件,目标文件。例如: ```sh build/ └── macosx └── x86_64 └── release └─test ``` 如果配置禁用此策略,那么生成的产物将会直接生成到 build 根目录下。变成: ```sh build/ └─ test ``` ## build.rpath 配置启用或者禁用构建时的 target rpath 设置。 默认情况下,如果 `target(foo)` 依赖动态库 bar,那么生成的 foo 可执行文件会自动加上 bar 的 rpath,这能保证用户直接执行 foo 程序,也能正确找到 bar。 如果你想禁用这个行为,可以显式配置禁用它。 ## install.rpath 尽管构建后的程序,会被设置 rpath,但是当 `xmake install` 安装后,它构建时候的 rpath 就不一定完全适用了,因此 xmake 会自动修改调整 rpath,使得安装后的程序,同样可以找到它的依赖库。 不过前提是,用户自己先得通过 `add_rpathdirs("/xxx", {installonly = true})` 去配置独立的安装 rpath。 而我们也可以通过这个 policy 去禁用默认的安装阶段 rpath 设置行为。 ## run.autobuild 这个策略用于调整 `xmake run` 的行为,默认情况下,执行 `xmake run` 并不会自动构建目标程序,如果程序还没被编译,就是提示用户手动构建一下。 而开启这个策略,我们就可以在运行程序前,先自动构建对应的目标程序。 ```sh $ xmake f --policies=run.autobuild $ xmake run ``` 如果想要全局生效这个策略,可以全局开启它。 ```sh $ xmake g --policies=run.autobuild ``` ## preprocessor.linemarkers 通常用户编译缓存中,预处理器的生成策略,默认开启,如果配置关闭这个策略,那么缓存生成的预处理文件内容将不包含 linemarkers 信息,这会极大减少预处理文件大小。 也会提升缓存的处理效率,但是缺点就是会丢失源码行信息,如果遇到编译错误,将无法看到准确的出错代码行。 ## preprocessor.gcc.directives\_only 这也是用于预处理器的策略,默认开启,这会提升 gcc 下编译缓存预处理的效率,但是如果源文件中包含 `__DATE__`, `__TIME__` 等宏,就会导致缓存出现不一致。 因此,可以根据自身工程代码,按需关闭此策略,确保生成的结果一致。 ## package.requires\_lock 可用于开启 `add_requires()` 引入的依赖包的版本锁定。 具体看下:[依赖包的锁定和升级](/zh/guide/package-management/using-official-packages#lock-and-upgrade-package)。 ## package.precompiled 可用于禁用 windows 下预编译依赖包的获取。 ## package.fetch\_only 如果开启这个策略,那么所有的依赖包仅仅只会从系统获取,不会从远程下载安装。 ## package.install\_only 如果开启这个策略,那么所有的依赖包仅仅只会走远程下载安装,不会从系统查找获取。 ## package.librarydeps.strict\_compatibility 默认禁用,如果启用它,那么当前包和它的所有库依赖包之间会保持严格的兼容性,任何依赖包的版本更新,都会强制触发当前包的重新编译安装。 以确保所有的包都是二进制兼容的,不会因为某个依赖包接口改动,导致和其他已被安装的其他包一起链接时候,发生链接和运行错误。 ```lua package("foo") add_deps("bar", "zoo") set_policy("package.librarydeps.strict_compatibility", true) ``` 例如,如果 bar 或者 zoo 的版本有更新,那么 foo 也会重新编译安装。 ## package.strict\_compatibility 默认禁用,如果启用它,那么当前包和其他所有依赖它的包之间会保持严格的兼容性,这个包的版本更新,都会强制触发其他父包的重新编译安装。 以确保所有的包都是二进制兼容的,不会因为某个依赖包接口改动,导致和其他已被安装的其他包一起链接时候,发生链接和运行错误。 ```lua package("foo") set_policy("package.strict_compatibility", true) package("bar") add_deps("foo") package("zoo") add_deps("foo") ``` 例如,如果 foo 的版本有更新,那么 bar 和 zoo 都会被强制重新编译安装。 ## package.install\_always 每次运行 `xmake f -c` 重新配置的时候,总是会重新安装包,这对于本地第三方源码包集成时候比较有用。 因为,用户可能随时需要修改第三方源码,然后重新编译集成它们。 之前只能通过每次修改包版本号,来触发重新编译,但是有了这个策略,就能每次都会触发重编。 ```lua add_rules("mode.debug", "mode.release") package("foo") add_deps("cmake") set_sourcedir(path.join(os.scriptdir(), "foo")) set_policy("package.install_always", true) on_install(function (package) local configs = {} table.insert(configs, "-DCMAKE_BUILD_TYPE=" .. (package:debug() and "Debug" or "Release")) table.insert(configs, "-DBUILD_SHARED_LIBS=" .. (package:config("shared") and "ON" or "OFF")) import("package.tools.cmake").install(package, configs) end) on_test(function (package) assert(package:has_cfuncs("add", {includes = "foo.h"})) end) package_end() add_requires("foo") target("demo") set_kind("binary") add_files("src/main.c") add_packages("foo") ``` ## package.download.http\_headers 设置包下载的 http headers 如果有些包的 url 下载,需要设置特定 http headers,才能通过下载,可以通过这个策略来指定。 ```lua package("xxx") set_policy("package.download.http_headers", "TEST1: foo", "TEST2: bar") ``` 我们也可以设置指定的 urls 的 http headers: ```lua add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", { http_headers = {"TEST1: foo", "TEST2: bar"} }) ``` ## windows.manifest.uac 通过这个策略,我们可以快速方便的设置并启用 Windows UAC。 它支持以下几个 Level: | Level | Flag | | --- | --- | | invoker | asInvoker | | admin | requireAdministrator | | highest | highestAvailable | 例如: ```lua set_policy("windows.manifest.uac", "admin") ``` 它等价于设置 ```lua if is_plat("windows") then add_ldflags("/manifest:embed", {"/manifestuac:level='requireAdministrator' uiAccess='false'"}, {force = true, expand = false}) end ``` 但是更加方便简洁,并且不需要判断平台,其他平台自动忽略。 ## windows.manifest.uac.ui 设置 Windows UAC 的 uiAccess,如果没有设置它,默认是 false。 ```lua set_policy("windows.manifest.uac.ui", true) ``` --- --- url: /zh/api/description/builtin-rules.md --- # 内置规则 {#builtin-rules} 自从2.2.1版本后,xmake提供了一些内置规则去简化日常xmake.lua描述,以及一些常用构建环境的支持。 我们可以通过运行以下命令,查看完整的内置规则列表: ```sh $ xmake show -l rules ``` ## mode.debug 为当前工程xmake.lua添加debug编译模式的配置规则,例如: ```lua add_rules("mode.debug") ``` 相当于: ```lua if is_mode("debug") then set_symbols("debug") set_optimize("none") end ``` 我们可以通过:`xmake f -m debug`来切换到此编译模式。 ## mode.release 为当前工程xmake.lua添加release编译模式的配置规则,例如: ```lua add_rules("mode.release") ``` ::: tip 注意 此模式默认不会带调试符号。 ::: 相当于: ```lua if is_mode("release") then set_symbols("hidden") set_optimize("fastest") set_strip("all") end ``` 我们可以通过:`xmake f -m release`来切换到此编译模式。 ## mode.releasedbg 为当前工程xmake.lua添加releasedbg编译模式的配置规则,例如: ```lua add_rules("mode.releasedbg") ``` ::: tip 注意 与release模式相比,此模式还会额外开启调试符号,这通常是非常有用的。 ::: 相当于: ```lua if is_mode("releasedbg") then set_symbols("debug") set_optimize("fastest") set_strip("all") end ``` 我们可以通过:`xmake f -m releasedbg`来切换到此编译模式。 ## mode.minsizerel 为当前工程xmake.lua添加minsizerel编译模式的配置规则,例如: ```lua add_rules("mode.minsizerel") ``` ::: tip 注意 与release模式相比,此模式更加倾向于最小代码编译优化,而不是速度优先。 ::: 相当于: ```lua if is_mode("minsizerel") then set_symbols("hidden") set_optimize("smallest") set_strip("all") end ``` 我们可以通过:`xmake f -m minsizerel`来切换到此编译模式。 ## mode.check 为当前工程xmake.lua添加check编译模式的配置规则,一般用于内存检测,例如: ```lua add_rules("mode.check") ``` 相当于: ```lua if is_mode("check") then set_symbols("debug") set_optimize("none") 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 if is_mode("profile") then set_symbols("debug") add_cxflags("-pg") add_ldflags("-pg") end ``` 我们可以通过:`xmake f -m profile`来切换到此编译模式。 ## mode.coverage 为当前工程xmake.lua添加coverage编译模式的配置规则,一般用于覆盖分析,例如: ```lua add_rules("mode.coverage") ``` 相当于: ```lua if is_mode("coverage") then add_cxflags("--coverage") add_mxflags("--coverage") add_ldflags("--coverage") end ``` 我们可以通过:`xmake f -m coverage`来切换到此编译模式。 ## mode.valgrind 此模式提供valgrind内存分析检测支持。 ```lua add_rules("mode.valgrind") ``` 我们可以通过:`xmake f -m valgrind`来切换到此编译模式。 ## mode.asan 此模式提供AddressSanitizer内存分析检测支持。 ```lua add_rules("mode.asan") ``` 我们可以通过:`xmake f -m asan`来切换到此编译模式。 ## mode.tsan 此模式提供ThreadSanitizer内存分析检测支持。 ```lua add_rules("mode.tsan") ``` 我们可以通过:`xmake f -m tsan`来切换到此编译模式。 ## mode.lsan 此模式提供LeakSanitizer内存分析检测支持。 ```lua add_rules("mode.lsan") ``` 我们可以通过:`xmake f -m lsan`来切换到此编译模式。 ## mode.ubsan 此模式提供UndefinedBehaviorSanitizer内存分析检测支持。 ```lua add_rules("mode.ubsan") ``` 我们可以通过:`xmake f -m ubsan`来切换到此编译模式。 ## qt.static 用于编译生成Qt环境的静态库程序: ```lua target("test") add_rules("qt.static") add_files("src/*.cpp") add_frameworks("QtNetwork", "QtGui") ``` ## qt.shared 用于编译生成Qt环境的动态库程序: ```lua target("test") add_rules("qt.shared") add_files("src/*.cpp") add_frameworks("QtNetwork", "QtGui") ``` ## qt.console 用于编译生成Qt环境的控制台程序: ```lua target("test") add_rules("qt.console") add_files("src/*.cpp") ``` ## qt.quickapp 用于编译生成Qt环境的Quick(qml) ui应用程序。 ```lua target("test") add_rules("qt.quickapp") add_files("src/*.cpp") add_files("src/qml.qrc") ``` ## qt.quickapp\_static 用于编译生成Qt环境的Quick(qml) ui应用程序(静态链接版本)。 ::: tip 注意 需要切换到静态库版本Qt SDK ::: ```lua target("test") add_rules("qt.quickapp_static") add_files("src/*.cpp") add_files("src/qml.qrc") ``` ## qt.widgetapp 用于编译Qt Widgets(ui/moc)应用程序 ```lua target("test") add_rules("qt.widgetapp") add_files("src/*.cpp") add_files("src/mainwindow.ui") add_files("src/mainwindow.h") -- 添加带有 Q_OBJECT 的meta头文件 ``` ## qt.widgetapp\_static 用于编译Qt Widgets(ui/moc)应用程序(静态库版本) ::: tip 注意 需要切换到静态库版本Qt SDK ::: ```lua target("test") add_rules("qt.widgetapp_static") add_files("src/*.cpp") add_files("src/mainwindow.ui") add_files("src/mainwindow.h") -- 添加带有 Q_OBJECT 的meta头文件 ``` 更多Qt相关描述见:[#160](https://github.com/xmake-io/xmake/issues/160) ## xcode.bundle 用于编译生成ios/macos bundle程序 ```lua target("test") add_rules("xcode.bundle") add_files("src/*.m") add_files("src/Info.plist") ``` ## xcode.framework 用于编译生成ios/macos framework程序 ```lua target("test") add_rules("xcode.framework") add_files("src/*.m") add_files("src/Info.plist") ``` ## xcode.application 用于编译生成ios/macos应用程序 ```lua target("test") add_rules("xcode.application") add_files("src/*.m", "src/**.storyboard", "src/*.xcassets") add_files("src/Info.plist") ``` ## 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"}) ``` ## plugin.vsxmake.autoupdate 我们可以使用此规则,在通过 `xmake project -k vsxmake` 生成的 vs 工程中,自动更新 vs 工程文件(当每次构建完成)。 ```lua add_rules("plugin.vsxmake.autoupdate") target("test") set_kind("binary") add_files("src/*.c") ``` ## plugin.compile\_commands.autoupdate 我们也可以使用这个规则来自动更新生成 `compile_commands.json` ```lua add_rules("plugin.compile_commands.autoupdate", {outputdir = ".vscode"}) target("test") set_kind("binary") add_files("src/*.c") ``` ## utils.symbols.export\_all v2.5.2 以上版本提供,我们可以用它自动导出所有的动态库符号,目前仅支持 windows dll 目标程序的符号导出,即使没有在代码中通过 `__declspec(dllexport)` 导出接口, xmake 也会自动导出所有 c/c++ 接口符号。 ```lua add_rules("mode.release", "mode.debug") target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_all") target("test") set_kind("binary") add_deps("foo") add_files("src/main.c") ``` c++ ```lua add_rules("utils.symbols.export_all", {export_classes = true}) ``` 相关 issue [#1123](https://github.com/xmake-io/xmake/issues/1123) 2.9.5 之后的版本还支持自定义过滤器,去针对性过滤需要导出的符号名和源文件名: ```lua target("bar") set_kind("shared") add_files("src/bar.cpp") add_rules("utils.symbols.export_all", {export_filter = function (symbol, opt) local filepath = opt.sourcefile or opt.objectfile if filepath and filepath:find("bar.cpp", 1, true) and symbol:find("add", 1, true) then print("export: %s at %s", symbol, filepath) return true end end}) ``` ## utils.symbols.export\_list 我们可以在 xmake.lua 里面直接定义导出的符号列表,例如: ```lua target("foo") set_kind("shared") add_files("src/foo.c") add_rules("utils.symbols.export_list", {symbols = { "add", "sub"}}) ``` 或者,在 `*.export.txt` 文件中添加导出的符号列表。 ```lua target("foo2") set_kind("shared") add_files("src/foo.c") add_files("src/foo.export.txt") add_rules("utils.symbols.export_list") ``` 完整的工程例子见:[导出符号例子](https://github.com/xmake-io/xmake/tree/dev/tests/projects/c/shared_library_export_list) ## utils.install.cmake\_importfiles v2.5.3 以上版本可以使用此规则在安装 target 目标库文件的时候,导出 .cmake 文件,用于其他 cmake 项目的库导入和查找。 ## utils.install.pkgconfig\_importfiles v2.5.3 以上版本可以使用此规则在安装 target 目标库文件的时候,导出 pkgconfig/.pc 文件,用于其他项目的库导入和查找。 ## utils.bin2c v2.5.7 以上版本可以使用此规则,在项目中引入一些二进制文件,并见他们作为 c/c++ 头文件的方式提供开发者使用,获取这些文件的数据。 比如,我们可以在项目中,内嵌一些 png/jpg 资源文件到代码中。 ```lua target("console") set_kind("binary") add_rules("utils.bin2c", {extensions = {".png", ".jpg"}}) add_files("src/*.c") add_files("res/*.png", "res/*.jpg") ``` ::: tip 注意 extensions 的设置是可选的,默认后缀名是 .bin ::: 然后,我们就可以通过 `#include "filename.png.h"` 的方式引入进来使用,xmake 会自动帮你生成对应的头文件,并且添加对应的搜索目录。 ```c static unsigned char g_png_data[] = { #include "image.png.h" }; int main(int argc, char** argv) { printf("image.png: %s, size: %d\n", g_png_data, sizeof(g_png_data)); return 0; } ``` 生成头文件内容类似: ```sh cat build/.gens/test/macosx/x86_64/release/rules/c++/bin2c/image.png.h 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x78, 0x6D, 0x61, 0x6B, 0x65, 0x21, 0x0A, 0x00 ``` ## utils.glsl2spv v2.6.1 以上版本可以使用此规则,在项目中引入 `*.vert/*.frag` 等 glsl shader 文件,然后实现自动编译生成 `*.spv` 文件。 另外,我们还支持以 C/C++ 头文件的方式,二进制内嵌 spv 文件数据,方便程序使用。 ### 编译生成 spv 文件 xmake 会自动调用 glslangValidator 或者 glslc 去编译 shaders 生成 .spv 文件,然后输出到指定的 `{outputdir = "build"}` 目录下。 ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {outputdir = "build"}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` 注,这里的 `add_packages("glslang")` 主要用于引入和绑定 glslang 包中的 glslangValidator,确保 xmake 总归能够使用它。 当然,如果用户自己系统上已经安装了它,也可以不用额外绑定这个包,不过我还是建议添加一下。 ### 编译生成 c/c++ 头文件 我们也可以内部借助 bin2c 模块,将编译后的 spv 文件生成对应的二进制头文件,方便用户代码中直接引入,我们只需要启用 `{bin2c = true}`。:w ```lua add_rules("mode.debug", "mode.release") add_requires("glslang", {configs = {binaryonly = true}}) target("test") set_kind("binary") add_rules("utils.glsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.vert", "src/*.frag") add_packages("glslang") ``` 然后我们可以在代码这么引入: ```c static unsigned char g_test_vert_spv_data[] = { #include "test.vert.spv.h" }; static unsigned char g_test_frag_spv_data[] = { #include "test.frag.spv.h" }; ``` 跟 bin2c 规则的使用方式类似,完整例子见:[glsl2spv example](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/glsl2spv) ## utils.hlsl2spv 除了 `utils.glsl2spv` 规则,我们现在还支持 `utils.hlsl2spv` 规则。 ```lua add_rules("mode.debug", "mode.release") add_requires("directxshadercompiler") target("test") set_kind("binary") add_rules("utils.hlsl2spv", {bin2c = true}) add_files("src/*.c") add_files("src/*.hlsl") add_packages("directxshadercompiler") ``` ## python.library 我们可以用这个规则,配合 pybind11 生成 python 库模块,它会调整 python 库的模块名。 ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library") add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` 带有 soabi: ```lua add_rules("mode.release", "mode.debug") add_requires("pybind11") target("example") add_rules("python.library", {soabi = true}) add_files("src/*.cpp") add_packages("pybind11") set_languages("c++11") ``` ## nodejs.module 构建 nodejs 模块。 ```lua add_requires("node-addon-api") target("foo") set_languages("cxx17") add_rules("nodejs.module") add_packages("node-addon-api") add_files("*.cc") end ``` ## utils.ipsc ipsc 编译器规则支持,使用方式如下: ```lua target("test") set_kind("binary") add_rules("utils.ispc", {header_extension = "_ispc.h"}) set_values("ispc.flags", "--target=host") add_files("src/*.ispc") add_files("src/*.cpp") ``` --- --- url: /zh/guide/package-management/package-distribution.md --- # 分发包 {#package-distribution} ## 定义包配置 {#define-package-configuration} ### 仓库包结构 在制作自己的包之前,我们需要先了解包仓库的结构,无论是官方包仓库还是自建私有包仓库,结构都是相同的: ``` xmake-repo - packages - t/tbox/xmake.lua - z/zlib/xmake.lua ``` 通过上面的结构,可以看到每个包都会有个 xmake.lua 用于描述它的安装规则,并且根据 `z/zlib` 两级子目录分类存储,方便快速检索。 ### 包描述说明 关于包的描述规则,基本上都是在它的 xmake.lua 里面完成的,这跟项目工程里的 xmake.lua 描述很类似,不同的是描述域仅支持 `package()`, 不过,在项目 xmake.lua 里面,也可以直接添加 `package()` 来内置包描述的,连包仓库都省了,有时候这样会更加方便。 首先,我们先拿 zlib 的描述规则,来直观感受下,这个规则可以在 [xmake-repo/z/zlib/xmake.lua](https://github.com/xmake-repo/z/zlib/xmake.lua) 下找到。 ``` package("zlib") set_homepage("http://www.zlib.net") set_description("A Massively Spiffy Yet Delicately Unobtrusive Compression Library") set_urls("http://zlib.net/zlib-$(version).tar.gz", "https://downloads.sourceforge.net/project/libpng/zlib/$(version)/zlib-$(version).tar.gz") add_versions("1.2.10", "8d7e9f698ce48787b6e1c67e6bff79e487303e66077e25cb9784ac8835978017") add_versions("1.2.11", "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1") on_install("windows", function (package) io.gsub("win32/Makefile.msc", "%-MD", "-" .. package:config("vs_runtime")) os.vrun("nmake -f win32\\Makefile.msc zlib.lib") os.cp("zlib.lib", package:installdir("lib")) os.cp("*.h", package:installdir("include")) end) on_install("linux", "macosx", function (package) import("package.tools.autoconf").install(package, {"--static"}) end) on_install("iphoneos", "android@linux,macosx", "mingw@linux,macosx", function (package) import("package.tools.autoconf").configure(package, {host = "", "--static"}) io.gsub("Makefile", "\nAR=.-\n", "\nAR=" .. (package:build_getenv("ar") or "") .. "\n") io.gsub("Makefile", "\nARFLAGS=.-\n", "\nARFLAGS=cr\n") io.gsub("Makefile", "\nRANLIB=.-\n", "\nRANLIB=\n") os.vrun("make install -j4") end) on_test(function (package) assert(package:has_cfuncs("inflate", {includes = "zlib.h"})) end) ``` 这个包规则对 windows、linux、macosx、iphoneos、mingw 等平台都添加了安装规则,基本上已经做到了全平台覆盖,甚至一些交叉编译平台,算是一个比较典型的例子了。 当然,有些包依赖源码实现力度,并不能完全跨平台,那么只需对它支持的平台设置安装规则即可。 更多详细的包配置 API 说明见:[包接口文档](/zh/api/description/package-dependencies) ### 扩展配置参数 详细见:[add\_configs](/zh/api/description/package-dependencies#add-configs) ### 内置配置参数 除了可以通过 [add\_configs](/zh/api/description/package-dependencies#add-configs) 设置一些扩展的配置参数以外,xmake 还提供了一些内置的配置参数,可以使用 #### 启用调试包 ```lua add_requires("xxx", {debug = true}) ``` 包描述里面必须有相关处理才能支持: ```lua on_install(function (package) local configs = {} if package:is_debug() then table.insert(configs, "--enable-debug") end import("package.tools.autoconf").install(package) end) ``` #### 设置msvc运行时库 ```lua add_requires("xxx", {configs = {vs_runtime = "MT"}}) ``` 通常情况下,通过 `import("package.tools.autoconf").install` 等内置工具脚本安装的包,内部都对 vs\_runtime 自动处理过了。 但是如果是一些特殊的源码包,构建规则比较特殊,那么需要自己处理了: ```lua on_install(function (package) io.gsub("build/Makefile.win32.common", "%-MD", "-" .. package:config("vs_runtime")) end) ``` ### 添加环境变量 对于一些库,里面也带了可执行的工具,如果需要在集成包的时候使用这些工具,也可以设置上对应 PATH 环境变量: ```lua package("luajit") on_load(function (package) if is_plat("windows") then package:addenv("PATH", "lib") end package:addenv("PATH", "bin") end) ``` 而在项目工程中,只有通过 `add_packages` 集成对应的包后,对应的环境变量才会生效。 ```lua add_requires("luajit") target("test") set_kind("binary") add_packages("luajit") after_run(function (package) os.exec("luajit --version") end) ``` ### 安装二进制包 xmake 也支持直接引用二进制版本包,直接安装使用,例如: ```lua if is_plat("windows") then set_urls("https://www.libsdl.org/release/SDL2-devel-$(version)-VC.zip") add_versions("2.0.8", "68505e1f7c16d8538e116405411205355a029dcf2df738dbbc768b2fe95d20fd") end on_install("windows", function (package) os.cp("include", package:installdir()) os.cp("lib/$(arch)/*.lib", package:installdir("lib")) os.cp("lib/$(arch)/*.dll", package:installdir("lib")) end) ``` ### 本地测试 如果在本地 xmake-repo 仓库中,已经添加和制作好了新的包,可以在本地运行测试下是否通过,如果测试通过,即可提交 PR 到官方仓库,请求 merge。 我们可以执行下面的脚本进行测试指定包: ```sh cd xmake-repo xmake l scripts/test.lua -v -D zlib ``` 上面的命令会强制重新下载和安装 zlib 包,测试整个安装流程是否 ok,加上 `-v -D` 是为了可以看到完整详细的日志信息和出错信息,方便调试分析。 如果网络环境不好,不想每次测试都去重新下载所有依赖,可以加上 `--shallow` 参数来执行,这个参数告诉脚本,仅仅重新解压本地缓存的 zlib 源码包,重新执行安装命令,但不会下载各种依赖。 ```sh cd xmake-repo xmake l scripts/test.lua -v -D --shallow zlib ``` 如果我们想测试其他平台的包规则是否正常,比如 android、iphoneos 等平台,可以通过 `-p/--plat` 或者 `-a/--arch` 来指定。 ```sh cd xmake-repo xmake l scripts/test.lua -v -D --shallow -p iphoneos -a arm64 zlib xmake l scripts/test.lua -v -D --shallow -p android --ndk=/xxxx zlib ``` ## 生成远程包 {#generate-remote-package} 除了本地包格式,`xmake package` 现在也支持生成远程包,便于用户将其快速提交到远程仓库。 我们只需要在打包的时候,修改包格式。 ```sh $ xmake package -f remote ``` ```lua [packages/f/foo/xmake.lua] package("foo") set_description("The foo package") set_license("Apache-2.0") add_deps("add", "sub") add_urls("https://github.com/myrepo/foo.git") add_versions("1.0", "") on_install(function (package) local configs = {} if package:config("shared") then configs.kind = "shared" end import("package.tools.xmake").install(package, configs) end) on_test(function (package) -- TODO check includes and interfaces -- assert(package:has_cfuncs("foo", {includes = "foo.h"}) end) ``` 包定义配置相比本地包,多了实际的安装逻辑,以及 urls 和 versions 的设置, 我们也能够通过附加参数,去修改 urls、versions 等配置值,例如: ```sh $ xmake package -f remote --url=https://xxxx/xxx.tar.gz --shasum=xxxxx --homepage=xxxxx` ``` xmake 也会从 target 的 `set_license` 和 `set_version` 等配置中读取相关配置信息。 ## 提交包到官方仓库 {#submit-packages-to-the-official-repository} 目前这个特性刚完成不久,目前官方仓库的包还不是很多,有些包也许还不支持部分平台,不过这并不是太大问题,后期迭代几个版本后,我会不断扩充完善包仓库。 如果你需要的包,目前的官方仓库还没有收录,可以提交 issues 或者自己可以在本地调通后,贡献提交到官方仓库:[xmake-repo](https://github.com/xmake-io/xmake-repo) 详细的贡献说明,见:[CONTRIBUTING.md](https://github.com/xmake-io/xmake-repo/blob/master/CONTRIBUTING.md) 关于如何制作使用自建私有包仓库,可以查看:[使用自建私有包仓库](/zh/guide/package-management/using-official-packages.html#using-self-built-private-package-repository)。 ## 分发和使用自定义包规则 {#custom-rule-distribution} 2.7.2 版本之后,我们可以在包管理仓库中,添加自定义构建规则脚本,实现跟随包进行动态下发和安装。 我们需要将自定义规则放到仓库的 `packages/x/xxx/rules` 目录中,它会跟随包一起被安装。 但是,它也存在一些限制: * 在包中规则,我们不能添加 `on_load`、`after_load` 脚本,但是通常可以使用 `on_config` 来代替。 ### 添加包规则 我们需要将规则脚本添加到 rules 固定目录下,例如:packages/z/zlib/rules/foo.lua ```lua rule("foo") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` ### 应用包规则 使用规则的方式跟之前类似,唯一的区别就是,我们需要通过 `@packagename/` 前缀去指定访问哪个包里面的规则。 具体格式:`add_rules("@packagename/rulename")`,例如:`add_rules("@zlib/foo")`。 ```lua add_requires("zlib", {system = false}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib") add_rules("@zlib/foo") ``` ### 通过包别名引用规则 如果存在一个包的别名,xmake 将优先考虑包的别名来获得规则。 ```lua add_requires("zlib", {alias = "zlib2", system = false}) target("test") set_kind("binary") add_files("src/*.cpp") add_packages("zlib2") add_rules("@zlib2/foo") ``` ### 添加包规则依赖 我们可以使用`add_deps("@bar")`来添加相对于当前包目录的其他规则。 然而,我们不能添加来自其他包的规则依赖,它们是完全隔离的,我们只能参考用户项目中由`add_requires`导入的其他包的规则。 packages/z/zlib/rules/foo.lua ```lua rule("foo") add_deps("@bar") on_config(function (target) print("foo: on_config %s", target:name()) end) ``` packages/z/zlib/rules/bar.lua ```lua rule("bar") on_config(function (target) print("bar: on_config %s", target:name()) end) ``` --- --- url: /zh/guide/extras/distributed-compilation.md --- # 分布式编译 {#distributed-compilation} Xmake 提供了内置的分布式编译服务,通常它可以与本地编译缓存、远程编译缓存相互配合,实现最优的编译加速。 另外,它是完全跨平台支持的,我们不仅支持 gcc/clang,也能够很好地支持 Windows 和 msvc。 对于交叉编译,只要交叉工具链支持,我们不要求服务器的系统环境,即使混用 linux、macOS 和 Windows 的服务器资源,也可以很好地实现分布式编译。 ## 开启服务 {#start-service} 我们可以指定 `--distcc` 参数来开启分布式编译服务,当然如果不指定这个参数,xmake 会默认开启所有服务端配置的服务。 这里我们假设有 2 台机器作为分布式的编译服务器集群,ip 地址分别是 192.168.22.168,192.168.22.169,两台服务器分别执行下面的脚本 ```sh $ xmake service --distcc : listening 0.0.0.0:9093 .. ``` 我们也可以在开启服务的同时,回显详细日志信息。 ```sh $ xmake service --distcc -vD : listening 0.0.0.0:9093 .. ``` ## 以 Daemon 模式开启服务 ```sh $ xmake service --distcc --start $ xmake service --distcc --restart $ xmake service --distcc --stop ``` ## 配置服务端 {#configure-server} 我们首先运行 `xmake service` 命令,它会自动生成一个默认的 `server.conf` 配置文件,存储到 `~/.xmake/service/server.conf`。 ```sh $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. an token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9693 .. ``` 然后,我们编辑它,修正每台服务器的监听端口(可选)。 ```sh $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` ## 配置客户端 {#configure-client} 客户端配置文件在 `~/.xmake/service/client.conf`,我们可以在里面配置客户端需要连接的服务器地址。 我们可以在 hosts 列表中配置多个服务器地址,以及对应的 token。 ::: tip 注意 分布式编译,推荐使用 token 认证模式,因为密码模式下每台服务器连接时都要输入一次密码,很繁琐。 ::: ```sh $cat ~/.xmake/service/client.conf { distcc_build = { hosts = { { connect = "192.168.22.168:9693", token = "590234653af52e91b9e438ed860f1a2b" }, { connect = "192.168.22.169:9693", token = "590234653af52e91b9e438ed860f1a2b" } } } } ``` ### 配置超时 {#configure-timeout} 默认情况下,客户端连接、收发数据都是无限等待不超时的,但是如果访问服务端的网络不稳定,有可能会导致访问卡死,这时可以配置超时来解决。 如果发生超时异常,就会自动退化到本地编译,不会永远卡死。 我们可以配置 `send_timeout`、`recv_timeout` 和 `connect_timeout` 三种超时,如果在根节点设置,那么所有客户端服务都会生效。 ```sh $ cat ~/.xmake/service/client.conf { send_timeout = 5000, recv_timeout = 5000, connect_timeout = 5000 } ``` 我们也可以仅针对当前分布式构建服务配置超时,其他服务还是默认超时。 ```sh $ cat ~/.xmake/service/client.conf { distcc_build = { send_timeout = 5000, recv_timeout = 5000, connect_timeout = 5000, } } ``` ::: tip 注意 服务端配置同样支持超时配置。 ::: ## 用户认证和授权 {#user-authorization} 关于用户认证和授权,可以参考 [远程编译/用户认证和授权](/zh/guide/extras/remote-compilation#user-authorization) 里面的详细说明,用法是完全一致的。 ## 连接服务器 {#connect-server} 配置完认证和服务器地址后,就可以输入下面的命令,将当前工程连接到配置的服务器上。 我们需要在连接时,输入 `--distcc`,指定仅连接分布式服务。 ```sh $ cd projectdir $ xmake service --connect --distcc : connect 127.0.0.1:9693 .. : 127.0.0.1:9693 connected! ``` 我们也可以同时连接多个服务,比如分布式编译和远程编译缓存服务。 ```sh $ xmake service --connect --distcc --ccache ``` ::: tip 注意 如果不带任何参数,默认连接的是远程编译服务。 ::: ## 分布式编译项目 {#build-project} 连接上服务器后,我们就可以像正常本地编译那样,进行分布式编译了,例如: ```sh $ xmake ... [ 93%]: cache compiling.release src/demo/network/unix_echo_client.c ----> local job [ 93%]: cache compiling.release src/demo/network/ipv6.c [ 93%]: cache compiling.release src/demo/network/ping.c [ 93%]: distcc compiling.release src/demo/network/unix_echo_server.c. ----> distcc job [ 93%]: distcc compiling.release src/demo/network/http.c [ 93%]: distcc compiling.release src/demo/network/unixaddr.c [ 93%]: distcc compiling.release src/demo/network/ipv4.c [ 94%]: distcc compiling.release src/demo/network/ipaddr.c [ 94%]: distcc compiling.release src/demo/math/fixed.c [ 94%]: distcc compiling.release src/demo/libm/float.c [ 95%]: cache compiling.release src/demo/libm/double.c [ 95%]: cache compiling.release src/demo/other/test.cpp [ 98%]: archiving.release libtbox.a [ 99%]: linking.release demo [100%]: build ok! ``` 其中,带有 distcc 字样的是远程编译任务,其他的都是本地编译任务。默认 xmake 还开启了本地编译缓存,对分布式编译结果进行缓存,避免频繁请求服务器。 另外,我们也可以开启远程编译缓存,与其他人共享编译缓存,进一步加速多人协同开发的编译。 ## 断开连接 {#disconnect} ```sh $ xmake service --disconnect --distcc ``` ## 指定并行编译任务数 {#configure-njobs} 我们先简单介绍下,目前根据主机 cpu core 数量默认计算的并行任务数: ```lua local default_njob = math.ceil(ncpu * 3 / 2) ``` 因此,如果不开启分布式编译,默认的最大并行编译任务数就是这个 default\_njob。 如果开启分布式编译后,默认的并行编译任务数就是: ```lua local maxjobs = default_njob + server_count * server_default_njob ``` ### 修改本地并行任务数 我们只需要通过 `-jN` 就能指定本地并行任务数,但是它不会影响服务端的并行任务数。 ```sh $ xmake -jN ``` ### 修改服务端并行任务数 如果要修改服务端的并行任务数,需要修改客户端的配置文件。 ```sh $cat ~/.xmake/service/client.conf { distcc_build = { hosts = { { connect = "127.0.0.1:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 8 <------- modify here }, { connect = "192.168.01:9693", token = "590234653af52e91b9e438ed860f1a2b", njob = 4 } } } } ``` 可以对每个服务器主机,添加 `njob = N` 参数配置,指定这台服务器可以提供的并行任务数。 ## 分布式编译 Android 项目 {#build-android-project} xmake 提供的分布式编译服务是完全跨平台的,并且支持 Windows, Linux, macOS, Android, iOS 甚至交叉编译。 如果要进行 Android 项目编译,只需要在服务端配置中,增加 `toolchains` 工具链配置,提供 NDK 的跟路径即可。 ```sh $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", toolchains = { ndk = { ndk = "~/files/android-ndk-r21e" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` 然后,我们就可以像正常本地编译那样,分布式编译 Android 项目,甚至可以配置多台 Windows, macOS, Linux 等不同的服务器主机,做为分布式编译服务的资源,来编译它。 只需要下载对应平台的 NDK 就行了。 ```sh $ xmake f -p android --ndk=~/files/xxxx $ xmake ``` ## 分布式编译 iOS 项目 {#build-ios-project} 编译 iOS 项目更加简单,因为 Xmake 通常能自动检测到 Xcode,所以只需要像正常本地一样,切一下平台到 ios 即可。 ```sh $ xmake f -p iphoneos $ xmake ``` ## 分布式交叉编译配置 {#cross-compilation} 如果要分布式交叉编译,我们需要在服务端配置工具链 sdk 路径,例如: ```sh $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9693", toolchains = { cross = { sdkdir = "~/files/arm-linux-xxx" <------------ here } }, workdir = "/Users/ruki/.xmake/service/server/distcc_build" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` 其中,toolchains 下,每一项对应一个工具链,这里配置为 `cross = {}` 交叉工具链,对应 `toolchain("cross")`。 工具链里面我们可以配置 `sdkdir`, `bindir`, `cross` 等等,对应 `toolchain("cross")` 里面的 `set_sdkdir`, `set_bindir` 和 `set_cross` 等接口配置。 如果交叉工具链比较规范,我们通常只需要配置 `sdkdir`,xmake 就能自动检测到。 而客户端编译也只需要指定 sdk 目录。 ```sh $ xmake f -p cross --sdk=/xxx/arm-linux-xxx $ xmake ``` ## 清理服务器缓存 {#clean-cache} 每个项目在服务端的编译,都会产生一些缓存文件,他们都是按工程粒度分别存储的,我们可以通过下面的命令,对当前工程清理每个服务器对应的缓存。 ```sh $ xmake service --clean --distcc ``` ## 一些内部优化 #{optimizations} 1. 缓存服务器端编译结果,避免重复编译 2. 本地缓存,远程缓存优化,避免不必要的服务端通信 3. 服务器负载均衡调度,合理分配服务器资源 4. 预处理后小文件直接本地编译,通常会更快 5. 大文件实时压缩传输,基于 lz4 快速压缩 6. 内部状态维护,相比 distcc 等独立工具,避免了频繁的独立进程加载耗时,也避免了与守护进程额外的通信 --- --- url: /zh/guide/basic-commands/switch-toolchains.md --- # 切换工具链 {#switch-toolchains} 我们可以通过传递 `--toolchain=[name]` 参数给 `xmake f/config` 命令,来实现全局的工具链切换。 :::tip 注意 这种方式是全局的,如果我们想要针对特定 target 进行工具链切换,我们需要在 xmake.lua 配置中,通过 [set\_toolchains](/zh/api/description/project-target#set-toolchains) 接口来实现。 ::: 如果我们想在 xmake.lua 工程配置文件中去切换它,可以到:[配置工具链](/zh/guide/project-configuration/toolchain-configuration) 进一步查看。 另外,Xmake 还内置提供了一些常用的工具链,可以直接切换使用,但前提是:用户自己已经在系统上安装了对应的工具链环境。 ## Gcc 如果 linux 上安装了 gcc 工具链,通常 xmake 都会优先检测使用,当然我们也可以手动切换到 gcc 来构建。 ```sh $ xmake f --toolchain=gcc -c $ xmake ``` ### 使用指定版本的 Gcc 如果用户额外安装了 gcc-11, gcc-10 等特定版本的 gcc 工具链,在本地的 gcc 程序命名可能是 `/usr/bin/gcc-11`。 一种办法是通过 `xmake f --cc=gcc-11 --cxx=gcc-11 --ld=g++-11` 挨个指定配置来切换,但非常繁琐。 所以,xmake 也提供了更加快捷的切换方式: ```sh $ xmake f --toolchain=gcc-11 -c $ xmake ``` 只需要指定 `gcc-11` 对应的版本名,就可以快速切换整个 gcc 工具链。 ## Clang 在 macOS 和 linux,通常 xmake 也会优先尝试去自动检测和使用它,当然我们也可以手动切换。 ```sh $ xmake f --toolchain=clang -c $ xmake ``` 在 windows 上,它会自动加载 msvc 环境。 另外,我们也支持 PortableBuildTools + clang 环境: ```sh $ xmake f -c --sdk=C:/BuildTools --toolchain=clang $ xmake -v [ 50%]: cache compiling.release src\main.cpp C:\Users\star\scoop\apps\llvm\current\bin\clang -c -Qunused-arguments -m64 --target=x86_64-windows-msvc -fexceptions -fcxx-exceptions -o build\.objs\test\windows\x64\release\src\main.cpp.obj src\main.cpp [ 75%]: linking.release test.exe C:\Users\star\scoop\apps\llvm\current\bin\clang++ -o build\windows\x64\release\test.exe build\.objs\test\windows\x64\release\src\main.cpp.obj -m64 --target=x86_64-windows-msvc [100%]: build ok, spent 0.235s ``` ## Clang-cl 如果只是单纯的切换使用 clang-cl.exe 编译器,剩下的链接操作还是用 msvc,那么我们不需要整个工具链切换,仅仅切换 c/c++ 编译器。 ```sh $ xmake f --cc=clang-cl --cxx=clang-cl -c $ xmake ``` 自 v2.7.2 起,也有专门的 clang-cl 工具链。使用 clang-cl 工具链相较于 msvc 工具链的优势在于,在 windows 上,`--vs_toolset` 选项会被正确处理。 ## LLVM 除了独立 clang 编译器,如果用户安装了完整 llvm 工具链,我们也可以整个切换过去,包括 `llvm-ar` 等工具。 ```sh $ xmake f --toolchain=llvm --sdk=/xxxx/llvm $ xmake ``` 如果是手动下载的 llvm sdk,我们需要额外指定 llvm sdk 根目录,确保 xmake 能找到它,当然,如果用户已经安装到 PATH 目录下,`--sdk` 参数的设置也是可选的。 ## Circle v2.5.9 xmake 新增了 circle 编译器的支持,这是个新的 C++20 编译器,额外附带了一些有趣的编译期元编程特性,有兴趣的同学可以到官网查看:https://www.circle-lang.org/ ```sh $ xmake f --toolchain=circle $ xmake ``` ## Tinyc [Tiny C 编译器](https://bellard.org/tcc/) 非常的轻量,在一些不想安装 msvc/llvm 等重量型编译器的情况下,使用它可能快速编译一些 c 代码。 ```sh $ xmake f --toolchain=tinycc $ xmake ``` 使用的时候,请先把 tinycc 编译器加入 PATH 环境。 我们也可以使用远程工具链自动下载集成它,真正做到全平台一键编译,无任何用户手动安装操作。 ```lua add_requires("tinycc") target("test") set_kind("binary") add_files("src/*.c) set_toolchains("@tinycc") ``` ## Armcc for Keil/MDK v2.5.9 新增了对 Keil/MDK 下 armcc 的工具链支持,相关 issue 见:[#1753](https://github.com/xmake-io/xmake/issues/1753) ```sh xmake f -p cross -a cortex-m3 --toolchain=armcc -c xmake ``` 这个工具链主要用于嵌入式交叉编译,所以指定了 `-p cross` 交叉编译平台,`-a cortex-m3` 指定使用的 cpu,这里复用了 `-a/--arch` 参数。 ## Armclang for Keil/MDK v2.5.9 新增了对 Keil/MDK 下 armclang 的工具链支持,相关 issue 见:[#1753](https://github.com/xmake-io/xmake/issues/1753) ```sh xmake f -p cross -a cortex-m3 --toolchain=armclang -c xmake ``` 这个工具链主要用于嵌入式交叉编译,所以指定了 `-p cross` 交叉编译平台,`-a cortex-m3` 指定使用的 cpu,这里复用了 `-a/--arch` 参数。 ## GNU-RM 另外一个嵌入式 arm 的交叉工具链,官网:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm ```sh $ xmake f --toolchain=gnu-rm -c $ xmake ``` ## SDCC 也是一个嵌入式的 arm 编译工具链。 ```sh $ xmake f --toolchain=sdcc -a stm8 $ xmake ``` 我们可以指定 `-a stm8` 切换 cpu 架构,目前支持的有: * stm8 * mcs51 * z80 * z180 * r2k * r3ka * s08 * hc08 ## Mingw mingw 工具链很常用,并且全平台都提供,我们可以仅仅切换相关工具链: ```sh $ xmake f --toolchain=mingw -c $ xmake ``` 但是这样,一些目标文件的后缀名并不完全匹配,因此建议整个切到 mingw 平台编译,还能支持依赖包下载。 ```sh $ xmake f -p mingw -c $ xmake ``` 如果 mingw 工具链安装在 linux、macOS 或 msys/mingw64 环境,通常都能自动检测到,如果检测到,也可以手动指定 mingw sdk 路径。 ```sh $ xmake f -p mingw --mingw=/xxx/mingw -c $ xmake ``` 注意,这里使用了 `--mingw` 而不是 `--sdk`。其实这两个都可以,但是使用 `--mingw` 单独的参数可以更好的保证其他交叉编译工具链不冲突。 ## LLVM-Mingw 这其实是一个独立于 Mingw 的项目,用法和 Mingw 完全一致,但是它是基于 LLVM 的,并且提供了 arm/arm64 等其他更多架构的支持,而不仅仅是 i386/x86\_64 ```sh $ xmake f -p mingw -a arm64 --mingw=/xxx/llvm-mingw -c $ xmake ``` 如果要使用 llvm-mingw 的 arm/arm64 架构,则需要额外指定 `-a arm64` 参数才行,另外 llvm-mingw 默认 xmake 不一定能够检测到,需要额外设置 sdk 路径。 ## Zig 如果要构建 Zig 程序,我们默认执行 xmake 就能自动使用 zig 工具链,但前提是 zig 已经在 PATH 环境下。 ```sh $ xmake ``` 当然,我们也可以手动设置它。 ```sh $ xmake f --toolchain=zig -c $ xmake ``` 也可以指定 zig 编译器的路径。 ```sh $ xmake f --toolchain=zig --zc=/xxxx/zig -c $ xmake ``` ### Zig CC 我们也可以使用 zig 提供的 `zig cc` 编译器去编译 C/C++ 代码。 ```sh $ xmake f --cc="zig cc" --cxx="zig cc" --ld="zig c++" -c $ xmake ``` ### 交叉编译 另外,我们也可以使用 zig 实现交叉编译。 ```sh $ xmake f -p cross --cross=riscv64-linux-musl --toolchain=zig $ xmake ``` 或者编译 arm64 架构: ```sh $ xmake f --toolchain=zig -a arm64 -c $ xmake ``` ## Emcc (WASM) 如果要编译 wasm 程序,我们只需要切换到 wasm 平台,默认就会使用 emcc 工具链去编译。 ```sh $ xmake f -p wasm $ xmake ``` ## Wasi (WASM) 这是另外一个启用了 WASI 的 Wasm 工具链,我们需要手动切换使用。 ```sh $ xmake f -p wasm --toolchain=wasi $ xmake ``` ## Icc (Intel C/C++ Compiler) 我们也可以切换到 Intel 的 C/C++ 编译器去使用。 ```sh $ xmake f --toolchain=icc -c $ xmake ``` ## Ifort (Intel Fortain Compiler) 我们也可以切换到 Intel 的 Fortran 编译器去使用。 ```sh $ xmake f --toolchain=ifort -c $ xmake ``` ## gfortran 除了 Intel 的 Fortran 编译器,我们还有 gnu fortran 编译器可用。 ```sh $ xmake f --toolchain=gfortran -c $ xmake ``` ## fpc (Free Pascal) 对于 pascal 程序,xmake 默认就会使用 fpc 编译器来编译。 ```sh $ xmake ``` 当然,我们也可以手动切换。 ```sh $ xmake f --toolchain=fpc -c $ xmake ``` ## Dlang 对于 dlang 程序,xmake 默认就会使用 dmd 编译器来编译。 ```sh $ xmake ``` 当然,我们也可以手动切换。 ```sh $ xmake f --toolchain=dlang -c $ xmake ``` 需要注意的是,此处的 dlang 工具链其实内部包含了对 `dmd`, `ldc2` 和 `gdc` 的自动探测和切换。 ## Cuda 对于 Cuda 程序,我们需要手动切换到 cuda 工具链。 ```sh $ xmake f --toolchain=cuda -c $ xmake ``` 我们也可以手动切换 nvcc 内部调用的 C/C++ 编译器。 ```sh $ xmake f --toolchain=cuda --cu-ccbin=clang -c $ xmake ``` ## 汇编器 关于独立的汇编器工具链,xmake 支持:yasm, nasm, fasm 三个,可以随意切换,如果没设置,默认使用 gcc/clang/msvc 自带的汇编器。 ```sh $ xmake f --toolchain=nasm -c $ xmake ``` 也可以单独指定汇编器路径 ```sh $ xmake f --toolchain=nasm --as=/xxx/nasm -c $ xmake ``` ## Go golang 编译工具链,默认编译 go 程序会自动启用。 ```sh $ xmake ``` ## Rust rust 编译工具链,默认编译 rust 程序会自动启用。 ```sh $ xmake ``` 目前 rust 工具链还可以支持 android 等交叉编译环境。 ```sh $ xmake f -p android --ndk=~/android-ndk-r20b -c $ xmake ``` ## NDK Android 的 NDK 编译工具链,只要启用 android 平台,就会默认启用。 ```sh $ xmake f -p android --ndk=~/android-ndk-r20b -c $ xmake ``` 如果 `--ndk` 参数不指定,xmake 也会默认从 AndroidSDK/ndk-bundle 目录,以及 `$ANDROID_NDK_HOME`, `ANDROID_NDK_ROOT` 等环境变量中去探测它。 另外,我们也可以设置导全局的 `xmake g --ndk=` 配置中,避免每次重复设置。 --- --- url: /zh/guide/basic-commands/create-project.md --- # 创建工程 {#create-project} ## 简介 Xmake 提供了一些常用命令,让用户方便快速地创建工程,以及配置,编译和运行。 所有的命令,可以通过 `xmake -h` 去查看,命令格式如下: ```sh xmake [action] [arguments] ... ``` 其中,action 就是 xmake cli 提供的子命令,而对于创建工程,它就是 `xmake create`。 ## 创建一个 C++ 空工程 首先,我们可以尝试创建一个名叫 `hello` 的 `c++` 控制台空工程。 ```sh $ xmake create hello create hello ... [+]: xmake.lua [+]: src/main.cpp [+]: .gitignore create ok! ``` 执行完后,将会生成一个简单工程结构。 ``` hello ├── src │   └─main.cpp └── xmake.lua ``` 这是一个最简单的工程,它会生成一个最基础的 xmake.lua 工程配置文件,通常它位于工程根目录,Xmake 会自动加载它。 ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("hello") set_kind("binary") add_files("src/*.cpp") -- FAQ -- ... ``` 此外,文件结尾还提供了一些注释,里面有提供常用的配置示例,方便快速查看。 然后,我们只需要进入刚刚创建的 hello 工程根目录,执行 xmake 命令,即可完成编译。 ```sh $ xmake [ 23%]: cache compiling.release src/main.cpp [ 47%]: linking.release hello [100%]: build ok, spent 2.696s ``` ## 指定语言 我们可以通过 `-l [languages]` 参数,去指定创建其他语言的工程,比如创建一个 C 语言工程。 ```sh $ xmake create -l c hello create hello ... [+]: xmake.lua [+]: src/main.c [+]: .gitignore create ok! ``` 或者创建一个 Rust 空工程。 ```sh $ xmake create -l rust hello create hello ... [+]: xmake.lua [+]: src/main.rs [+]: .gitignore create ok! ``` 完整语言可以通过 `xmake create -h` 查看。 ```sh -l LANGUAGE, --language=LANGUAGE The project language (default: c++) - pascal - c++ - zig - go - nim - dlang - cuda - rust - kotlin - vala - swift - fortran - objc - c - objc++ ``` ## 指定工程模板 另外,我们也可以通过 `-t [template]` 参数,去指定需要创建的工程模板类型。 例如,创建一个静态库工程: ```sh $ xmake create -t static test create test ... [+]: xmake.lua [+]: src/foo.cpp [+]: src/foo.h [+]: src/main.cpp [+]: .gitignore create ok! ``` ```sh $ xmake [ 23%]: cache compiling.release src/main.cpp [ 23%]: cache compiling.release src/foo.cpp [ 35%]: linking.release libfoo.a [ 71%]: linking.release test [100%]: build ok, spent 1.795s ``` 完整模板列表,也可以通过 `xmake create -h` 来查看。 ```sh -t TEMPLATE, --template=TEMPLATE Select the project template id or name of the given language. (default: console) - console: pascal, c++, zig, go, nim, dlang, cuda, rust, kotlin, vala, swift, fortran, objc, c, objc++ - module.binary: c++, c - module.shared: c++, c - qt.console: c++ - qt.quickapp: c++ - qt.quickapp_static: c++ - qt.shared: c++ - qt.static: c++ - qt.widgetapp: c++ - qt.widgetapp_static: c++ - shared: pascal, c++, zig, nim, dlang, cuda, kotlin, vala, fortran, c - static: c++, zig, go, nim, dlang, cuda, rust, kotlin, vala, fortran, c - tbox.console: c++, c - tbox.shared: c++, c - tbox.static: c++, c - wxwidgets: c++ - xcode.bundle: objc, objc++ - xcode.framework: objc, objc++ - xcode.iosapp: objc - xcode.iosapp_with_framework: objc - xcode.macapp: objc - xcode.macapp_with_framework: objc - xmake.cli: c++, c ``` 其中,最常用的就是创建控制台(console)、静态库(static)和动态库(shared)等程序。 --- --- url: /zh/posts/lua-profiler.md --- 之前在给[xmake](https://xmake.io/zh/)做构建的效率优化的时候,需要对lua脚本的api调用性能进行分析,分析出最耗时一些lua调用api, 找出性能瓶颈,来针对性地进行优化,那么问题来了,如果对lua脚本像c程序那样进行profile呢? 我们现在看下最后实现完的最终效果: ``` 4.681, 98.84%, 1, anonymous : actions/build/main.lua: 36 3.314, 69.98%, 1, anonymous : actions/build/main.lua: 66 3.314, 69.98%, 1, build : actions/build/builder.lua: 127 3.298, 69.65%, 2, _build_target : actions/build/builder.lua: 41 3.298, 69.65%, 2, script : actions/build/builder.lua: 30 2.590, 54.70%, 2, buildall : actions/build/kinds/object.lua: 174 2.239, 47.27%, 468, resume : core/sandbox/modules/coroutine.lua: 40 2.226, 47.00%, 468, anonymous : actions/build/kinds/object.lua: 242 2.073, 43.77%, 3, _build_target_and_deps : actions/build/builder.lua: 64 2.047, 43.22%, 468, _build : actions/build/kinds/object.lua: 79 2.034, 42.96%, 1, build : actions/build/kinds/static.lua: 31 1.190, 25.13%, 1, build : actions/build/kinds/binary.lua: 31 0.806, 17.03%, 8, load : core/base/interpreter.lua: 527 0.766, 16.18%, 2, run : core/project/task.lua: 393 0.711, 15.01%, 1, anonymous : actions/config/main.lua: 132 0.615, 12.99%, 2117, vformat : core/sandbox/modules/string.lua: 40 0.593, 12.53%, 16, defaults : core/base/option.lua: 803 0.593, 12.52%, 1, save : core/base/option.lua: 131 0.475, 10.03%, 2, anonymous : /Users/ruki/projects/personal/tbox/xmake.lua: 0 ``` 其中第一列为当前调用的耗时(单位:cpu时钟数),第二列为耗时占比,第三列为调用次数,然后是函数名和源代码位置。 #### debug.sethook简介 其实lua自带的debug就可以做到: ``` debug库提供了一种hook的方式,可以通过注册一个handler函数,在lua脚本运行到某个调用时,会触发这个handler, 获取到相应的执行信息,并且给你一个记录和数据维护的机会。 ``` 它主要有四种事件会触发这个handler的调用: 1. 当调用一个lua函数的时候,会触发call事件 2. 当函数返回的时候,会触发一个return事件 3. 当执行下一行代码的时候,会触发一个line事件 4. 当运行指定数目的指令后,会触发count事件 我们可以通过`debug.sethook`这个函数来注册一个hook的handler,他有三个参数: 1. handler的处理函数,hook事件触发后被调用 2. 描述需要hook的事件类型,call、return和line事件分别对应:'c', 'r', 'l',可以互相组合成一个字符串 3. 获取count事件的频率(可选) 如果需要 要想关掉hooks,只需要不带参数地调用sethook即可。 例如: 最简单的trace,仅仅打印每条执行语句的行号: ```lua debug.sethook(print, "l") ``` 显示结果如下: ``` line 136 line 113 line 76 line 77 line 113 line 118 ``` 我们也可以自定义一个handler,传入第一个参数,通过debug库的getinfo获取正在执行的代码文件路径: ```lua debug.sethook(function (event, line) print(debug.getinfo(2).short_src .. ":" .. line) end, "l") ``` 显示结果如下: ``` /usr/local/share/xmake/core/base/path.lua:46 /usr/local/share/xmake/core/base/path.lua:47 /usr/local/share/xmake/core/base/path.lua:56 /usr/local/share/xmake/core/base/string.lua:32 /usr/local/share/xmake/core/base/string.lua:33 /usr/local/share/xmake/core/base/string.lua:34 /usr/local/share/xmake/core/base/string.lua:35 /usr/local/share/xmake/core/base/string.lua:36 /usr/local/share/xmake/core/base/string.lua:38 /usr/local/share/xmake/core/base/string.lua:33 ``` 如果需要禁用之前的hook,只需要调用: ```lua debug.sethook() ``` #### profiler性能分析器的实现 实现一个profiler类,通过下面的方式进行记录: ```lua -- 开始记录 profiler.start() -- TODO -- ... -- 结束记录 profiler.stop() ``` 相关实现代码如下: ```lua -- start profiling function profiler:start(mode) -- 初始化报告 self._REPORTS = {} self._REPORTS_BY_TITLE = {} -- 记录开始时间 self._STARTIME = os.clock() -- 开始hook,注册handler,记录call和return事件 debug.sethook(profiler._profiling_handler, 'cr', 0) end -- stop profiling function profiler:stop(mode) -- 记录结束时间 self._STOPTIME = os.clock() -- 停止hook debug.sethook() -- 记录总耗时 local totaltime = self._STOPTIME - self._STARTIME -- 排序报告 table.sort(self._REPORTS, function(a, b) return a.totaltime > b.totaltime end) -- 格式化报告输出 for _, report in ipairs(self._REPORTS) do -- calculate percent local percent = (report.totaltime / totaltime) * 100 if percent < 1 then break end -- trace utils.print("%6.3f, %6.2f%%, %7d, %s", report.totaltime, percent, report.callcount, report.title) end end ``` 实现很简单,主要就是记录开始和结束时间,然后排序下最终的报表,进行格式化打印输出。 其中,计时函数使用了`os.clock`接口,返回一个程序使用CPU时间的一个近似值,不是毫秒哦,我们这边仅仅是为了分析性能瓶颈。 就算不获取精确毫秒数,也是可以的(其实用毫秒也没什么意义,这种debug.sethook的方式原本就不是很精确),只要通过耗时占比就可以分析。 接下来,就是handler函数中,对call和return事件,进行分别处理,累计每个函数的调用总时间,调用总次数。 ```lua -- profiling call function profiler:_profiling_call(funcinfo) -- 获取当前函数对应的报告,如果不存在则初始化下 local report = self:_func_report(funcinfo) assert(report) -- 记录这个函数的起始调用事件 report.calltime = os.clock() -- 累加这个函数的调用次数 report.callcount = report.callcount + 1 end -- profiling return function profiler:_profiling_return(funcinfo) -- 记录这个函数的返回时间 local stoptime = os.clock() -- 获取当前函数的报告 local report = self:_func_report(funcinfo) assert(report) -- 计算和累加当前函数的调用总时间 if report.calltime and report.calltime > 0 then report.totaltime = report.totaltime + (stoptime - report.calltime) report.calltime = 0 end end -- the profiling handler function profiler._profiling_handler(hooktype) -- 获取当前函数信息 local funcinfo = debug.getinfo(2, 'nS') -- 根据事件类型,分别处理 if hooktype == "call" then profiler:_profiling_call(funcinfo) elseif hooktype == "return" then profiler:_profiling_return(funcinfo) end end ``` 简单吧,最后就是通过函数,获取指定的报告了,这个就不多说了,简单贴下代码吧: ```lua -- get the function title function profiler:_func_title(funcinfo) -- check assert(funcinfo) -- the function name local name = funcinfo.name or 'anonymous' -- the function line local line = string.format("%d", funcinfo.linedefined or 0) -- the function source local source = funcinfo.short_src or 'C_FUNC' if os.isfile(source) then source = path.relative(source, xmake._PROGRAM_DIR) end -- make title return string.format("%-30s: %s: %s", name, source, line) end -- get the function report function profiler:_func_report(funcinfo) -- get the function title local title = self:_func_title(funcinfo) -- get the function report local report = self._REPORTS_BY_TITLE[title] if not report then -- init report report = { title = self:_func_title(funcinfo) , callcount = 0 , totaltime = 0 } -- save it self._REPORTS_BY_TITLE[title] = report table.insert(self._REPORTS, report) end -- ok? return report end ``` 需要注意的是,通过debug.sethook的方式,进行hook计时本身也是有性能损耗的,因此不可能完全精确,如果改用c实现也许会好些。 不过,对于平常的性能瓶颈分析,应该够用了。。 #### 结语 这里只是一个简单的例子,稍微扩展下,还是可以实现lua脚本的api实时调用追踪(也就是trace)。 完整代码,可以到xmake的源码中翻看:[profiler代码](https://github.com/xmake-io/xmake/blob/master/xmake/core/base/profiler.lua) 里面除了性能分析,trace调用也有实现。 最后,如果大家想快速体验下这个profiler的效果,可以直接运行xmake: ``` xmake --profile ``` 这个`--profile`是给xmake调试分析的时候使用,一般也就我自己用用,发现某些xmake操作很慢,想要查找问题原因的时候,不需要改代码,只需要快速的加上这个参数,重跑下就行了。。 顺带的提下,xmake另外两个调试相关的参数: 1. `-v/--verbose`:显示详细输出信息,编译时还会显示详细警告信息。 2. `--backtrace`:出错的时候,显示详细栈信息,用于快速issues反馈,辅助定位问题。 lua的debug库还是非常强大的,有兴趣的同学可以进一步去挖掘debug的各种特性哦。 --- --- url: /zh/posts/run-debug.md --- xmake默认在编译完程序后,可以通过以下命令运行指定目标程序: ```bash $xmake run [target] [arguments] ... ``` 并且在linux/macosx下面,目前已经支持关联调试器,去直接调试指定目标了,只需要加上`-d/--debug`参数选项: ```bash $xmake run -d [target] [arguments] ... ``` 默认情况下,xmake在macosx下用的是lldb,在linux下用的是gdb,调试器xmake会在配置的时候去自动检测,如果需要指定调试器路径,可以手动去配置它: ```bash $xmake f --debugger=/usr/bin/gdb $xmake run -d demo ``` 需要注意的是,目前windows上还没有支持,不过已经在计划中了,后续不久就会去支持它。。 --- --- url: /zh/api/description/package-dependencies.md --- # 包依赖 {#package-dependencies} ## package * 仓库依赖包定义描述 #### 函数原型 ::: tip API ```lua package(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 包名称字符串 | #### 用法说明 可先参考官方仓库中现有包描述:[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:add("includedirs", "include/libxml2") package:add("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) ``` ## set\_homepage * 设置包所在项目的官方页面地址 #### 函数原型 ::: tip API ```lua set_homepage(url: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | url | 包主页URL字符串 | #### 用法说明 设置包所在项目的官方页面地址。 ## set\_description * 设置包的相关描述信息 #### 函数原型 ::: tip API ```lua set_description(description: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | description | 包描述字符串 | #### 用法说明 一般通过`xmake require --info zlib`查看相关包信息时候,会看到。 ## set\_kind * 设置包类型 #### 函数原型 ::: tip API ```lua set_kind(kind: , { headeronly = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | kind | 包类型:"library", "binary", "toolchain" | | headeronly | 对于library类型,是否为仅头文件库 | 用于设置包的类型,xmake 包目前支持以下几种类型: ### library 这是默认的包类型,通常不需要显式配置。用于普通的库包,包括静态库和动态库。 ```lua package("zlib") -- library 类型,不需要显式设置 set_homepage("http://www.zlib.net") set_description("A Massively Spiffy Yet Delicately Unobtrusive Compression Library") ``` 对于 header-only 库(仅包含头文件的库),需要显式配置: ```lua package("fmt") set_kind("library", {headeronly = true}) set_homepage("https://fmt.dev") set_description("A modern formatting library") ``` ### binary 用于可执行程序包,这类包安装后会提供可执行文件,一般运行于当前编译主机的系统。 ```lua package("cmake") set_kind("binary") set_homepage("https://cmake.org") set_description("A cross-platform family of tool designed to build, test and package software") ``` ### toolchain 用于完整的编译工具链包,这类包包含完整的编译工具链(如编译器、链接器等),可以与 `set_toolchains` + `add_requires` 配合使用,实现工具链的自动下载和绑定。 ```lua package("llvm") set_kind("toolchain") set_homepage("https://llvm.org/") set_description("The LLVM Compiler Infrastructure") ``` 使用工具链包的示例: ```lua add_rules("mode.debug", "mode.release") add_requires("llvm 14.0.0", {alias = "llvm-14"}) target("test") set_kind("binary") add_files("src/*.c") set_toolchains("llvm@llvm-14") ``` ## set\_urls * 设置包源地址 #### 函数原型 ::: tip API ```lua set_urls(urls: , ..., { excludes = , version = , http_headers = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | urls | 包源URL字符串或数组 | | ... | 可变参数,可传入多个URL | | excludes | 解压时要排除的文件 | | version | 版本转换函数 | | http\_headers | 下载时的HTTP头 | 设置包的源码包或者git仓库地址,跟add\_urls不同的是,此接口是覆盖性设置,而add\_urls是追加设置,其他使用方式类似,这个根据不同需要来选择。 ## add\_urls * 添加包源地址 #### 函数原型 ::: tip API ```lua add_urls(urls: , ..., { alias = , excludes = , version = , http_headers = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | urls | 包源URL字符串或数组 | | ... | 可变参数,可传入多个URL | | alias | 不同源的URL别名 | | excludes | 解压时要排除的文件 | | version | 版本转换函数 | | http\_headers | 下载时的HTTP头 | #### 用法说明 添加包的源码包或者git仓库地址,此接口一般跟add\_version配对使用,用于设置每个源码包的版本和对应的sha256值或者git的commit或者tag或者branch。 ::: tip 注意 可以通过添加多个urls作为镜像源,xmake会自动检测优先选用最快的url进行下载,如果下载失败则会尝试其他urls。 ::: ```lua add_urls("https://github.com/protobuf-c/protobuf-c/releases/download/v$(version)/protobuf-c-$(version).tar.gz") add_versions("1.3.1", "51472d3a191d6d7b425e32b612e477c06f73fe23e07f6a6a839b11808e9d2267") ``` urls里面的`$(version)`内置变量,会根据实际安装时候选择的版本适配进去,而版本号都是从`add_versions`中指定的版本列表中选择的。 如果对于urls里面带有比较复杂的版本串,没有跟add\_versions有直接对应关系,则需要通过下面的方式定制化转换下: ```lua add_urls("https://sqlite.org/2018/sqlite-autoconf-$(version)000.tar.gz", {version = function (version) return version:gsub("%.", "") end}) add_versions("3.24.0", "d9d14e88c6fb6d68de9ca0d1f9797477d82fc3aed613558f87ffbdbbc5ceb74a") add_versions("3.23.0", "b7711a1800a071674c2bf76898ae8584fc6c9643cfe933cfc1bc54361e3a6e49") ``` 当然,我们也只可以添加git源码地址: ```lua add_urls("https://gitlab.gnome.org/GNOME/libxml2.git") ``` 如果设置的多个镜像地址对应的源码包sha256是不同的,我们可以通过alias的方式来分别设置 ```lua add_urls("https://ffmpeg.org/releases/ffmpeg-$(version).tar.bz2", {alias = "home"}) add_urls("https://github.com/FFmpeg/FFmpeg/archive/n$(version).zip", {alias = "github"}) add_versions("home:4.0.2", "346c51735f42c37e0712e0b3d2f6476c86ac15863e4445d9e823fe396420d056") add_versions("github:4.0.2", "4df1ef0bf73b7148caea1270539ef7bd06607e0ea8aa2fbf1bb34062a097f026") ``` 我们也可以设置指定的 urls 的 http headers: ```lua add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz", { http_headers = {"TEST1: foo", "TEST2: bar"} }) ``` ## add\_versions * 设置每个源码包的版本 #### 函数原型 ::: tip API ```lua add_versions(version: , hash: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 包版本字符串 | | hash | 用于验证的SHA256哈希值 | #### 用法说明 它也会设置对应的sha256值,具体描述见:[add\_urls](#add_urls) ## add\_versionfiles * 添加包版本列表 #### 函数原型 ::: tip API ```lua add_versionfiles(file: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | file | 包含版本和哈希值对的文件路径 | #### 用法说明 通常我们可以通过 `add_versions` 接口添加包版本,但是如果版本越来越多,就会导致包配置太过臃肿,这个时候,我们可以使用 `add_versionfiles` 接口将所有的版本列表,存储到单独的文件中去维护。 例如: ```lua package("libcurl") add_versionfiles("versions.txt") ``` ```sh 8.5.0 ce4b6a6655431147624aaf582632a36fe1ade262d5fab385c60f78942dd8d87b 8.4.0 e5250581a9c032b1b6ed3cf2f9c114c811fc41881069e9892d115cc73f9e88c6 8.0.1 9b6b1e96b748d04b968786b6bdf407aa5c75ab53a3d37c1c8c81cdb736555ccf 7.87.0 5d6e128761b7110946d1276aff6f0f266f2b726f5e619f7e0a057a474155f307 7.31.0 a73b118eececff5de25111f35d1d0aafe1e71afdbb83082a8e44d847267e3e08 ... ``` ## add\_patches * 设置包补丁 #### 函数原型 ::: tip API ```lua add_patches(version: , url: , hash: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 补丁适用的包版本 | | url | 补丁文件URL | | hash | 补丁验证的SHA256哈希值 | #### 用法说明 此接口用于针对源码包,在编译安装前,先打对应设置的补丁包,再对其进行编译,并且可支持同时打多个补丁。 ```lua if is_plat("macosx") then add_patches("1.15", "https://raw.githubusercontent.com/Homebrew/patches/9be2793af/libiconv/patch-utf8mac.diff", "e8128732f22f63b5c656659786d2cf76f1450008f36bcf541285268c66cabeab") end ``` 例如,上面的代码,就是针对macosx下编译的时候,打上对应的patch-utf8mac.diff补丁,并且每个补丁后面也是要设置sha256值的,确保完整性。 ## add\_links * 设置库链接 #### 函数原型 ::: tip API ```lua add_links(links: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | links | 库链接名称字符串或数组 | | ... | 可变参数,可传入多个链接名称 | #### 用法说明 默认情况下,xmake会去自动检测安装后的库,设置链接关系,但是有时候并不是很准,如果要自己手动调整链接顺序,以及链接名,则可以通过这个接口来设置。 ```lua add_links("mbedtls", "mbedx509", "mbedcrypto") ``` ## add\_syslinks * 设置系统库链接 #### 函数原型 ::: tip API ```lua add_syslinks(syslinks: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | syslinks | 系统库名称字符串或数组 | | ... | 可变参数,可传入多个系统库名称 | #### 用法说明 添加一些系统库链接,有些包集成链接的时候,还需要依赖一些系统库,才能链接通过,这个时候可以在包描述里面都附加上去。 ```lua if is_plat("macosx") then add_frameworks("CoreGraphics", "CoreFoundation", "Foundation") elseif is_plat("windows") then add_defines("CAIRO_WIN32_STATIC_BUILD=1") add_syslinks("gdi32", "msimg32", "user32") else add_syslinks("pthread") end ``` ## add\_linkorders * 调整包内部的链接顺序 #### 函数原型 ::: tip API ```lua add_linkorders(orders: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | orders | 链接顺序字符串或数组 | | ... | 可变参数,可传入多个顺序规格 | #### 用法说明 具体详情可以看下 target 内部对 `add_linkorders` 的文档说明,[target:add\_linkorders](/zh/api/description/project-target#add_linkorders)。 ```lua package("libpng") add_linkorders("png16", "png", "linkgroup::foo") add_linkgroups("dl", {name = "foo", group = true}) ``` ## add\_linkgroups * 配置包的链接组 #### 函数原型 ::: tip API ```lua add_linkgroups(groups: , ..., { name = , group = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | groups | 链接组名称字符串或数组 | | ... | 可变参数,可传入多个组名称 | | name | 用于链接的组名称 | | group | 是否作为组处理 | #### 用法说明 具体详情可以看下 target 内部对 `add_linkgroups` 的文档说明,[target:add\_linkgroups](/zh/api/description/project-target#add-linkgroups)。 ```lua package("libpng") add_linkorders("png16", "png", "linkgroup::foo") add_linkgroups("dl", {name = "foo", group = true}) ``` ## add\_frameworks * 添加依赖的系统 frameworks 链接 #### 函数原型 ::: tip API ```lua add_frameworks(frameworks: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | frameworks | 框架名称字符串或数组 | | ... | 可变参数,可传入多个框架名称 | #### 用法说明 示例见:[add\_syslinks](#add_syslinks) ## add\_linkdirs * 添加链接目录 #### 函数原型 ::: tip API ```lua add_linkdirs(dirs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dirs | 链接目录路径字符串或数组 | | ... | 可变参数,可传入多个目录路径 | #### 用法说明 包的链接库搜索目录也是可以调整的,不过通常都不需要,除非一些库安装完不在prefix/lib下面,而在lib的子目录下,默认搜索不到的话。 ## add\_includedirs * 添加其他头文件搜索目录 #### 函数原型 ::: tip API ```lua add_includedirs(dirs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dirs | 头文件目录路径字符串或数组 | | ... | 可变参数,可传入多个目录路径 | #### 用法说明 添加其他头文件搜索目录,用于指定包的头文件位置。 ## add\_bindirs * 添加可执行文件目录 #### 函数原型 ::: tip API ```lua add_bindirs(dirs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dirs | 可执行文件目录路径字符串或数组 | | ... | 可变参数,可传入多个目录路径 | #### 用法说明 默认情况下,如果配置了 `set_kind("binary")` 或者 `set_kind("toolchain")` 作为可执行的包。 那么,它默认会将 bin 目录作为可执行目录,并且自动将它加入到 PATH 环境变量中去。 而如果对应 library 包,想要将里面附带编译的一些可执行工具开放给用户执行,那么需要在包中配置 `package:addenv("PATH", "bin")` 中才行。 而通过这个接口去配置 `add_bindirs("bin")` ,那么将会自动将 bin 添加到 PATH,不再需要单独配置 PATH,另外,这也提供了一种可以修改可执行目录的方式。 ## add\_defines * 添加宏定义 #### 函数原型 ::: tip API ```lua add_defines(defines: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | defines | 宏定义字符串或数组 | | ... | 可变参数,可传入多个定义 | #### 用法说明 可以对集成的包对外输出一些特定的定义选项。 ## add\_configs * 添加包配置 #### 函数原型 ::: tip API ```lua add_configs(name: , { description = , default = , values = , type = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置参数名称 | | description | 配置描述字符串 | | default | 配置的默认值 | | values | 允许的值数组 | | type | 配置类型:"string", "boolean", "number" | #### 用法说明 我们可以通过此接口添加每个包的对外输出配置参数: ```lua package("pcre2") set_homepage("https://www.pcre.org/") set_description("A Perl Compatible Regular Expressions Library") add_configs("bitwidth", {description = "Set the code unit width.", default = "8", values = {"8", "16", "32"}}) on_load(function (package) local bitwidth = package:config("bitwidth") or "8" package:add("links", "pcre2-" .. bitwidth) package:add("defines", "PCRE2_CODE_UNIT_WIDTH=" .. bitwidth) end) ``` 在工程项目里面,我们也可以查看特定包的可配置参数和值列表: ```sh $ xmake require --info pcre2 The package info of project: require(pcre2): -> description: A Perl Compatible Regular Expressions Library -> version: 10.31 ... -> configs: -> bitwidth: -> description: Set the code unit width. -> values: {"8","16","32"} -> default: 8 ``` 然后在项目里面,启用这些配置,编译集成带有特定配置的包: ```lua add_requires("pcre2", {configs = {bitwidth = 16}}) ``` ## add\_extsources * 添加扩展的包源 #### 函数原型 ::: tip API ```lua add_extsources(sources: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sources | 外部源字符串或数组,格式:"pkgconfig::name" 或 "brew::name" | #### 用法说明 添加扩展的包源,用于指定包的外部源。 2.5.2 版本开始,我们也新增了 `add_extsources` 和 `on_fetch` 两个配置接口,可以更好的配置 xmake 在安装 C/C++ 包的过程中,对系统库的查找过程。 至于具体背景,我们可以举个例子,比如我们在 [xmake-repo](https://github.com/xmake-io/xmake-repo) 仓库新增了一个 `package("libusb")` 的包。 那么用户就可以通过下面的方式,直接集成使用它: ```lua add_requires("libusb") target("test") set_kind("binary") add_files("src/*.c") add_packages("libusb") ``` 如果用户系统上确实没有安装 libusb,那么 xmake 会自动下载 libusb 库源码,自动编译安装集成,没啥问题。 但如果用户通过 `apt install libusb-1.0` 安装了 libusb 库到系统,那么按理 xmake 应该会自动优先查找用户安装到系统环境的 libusb 包,直接使用,避免额外的下载编译安装。 但是问题来了,xmake 内部通过 `find_package("libusb")` 并没有找打它,这是为什么呢?因为通过 apt 安装的 libusb 包名是 `libusb-1.0`, 而不是 libusb。 我们只能通过 `pkg-config --cflags libusb-1.0` 才能找到它,但是 xmake 内部的默认 find\_package 逻辑并不知道 `libusb-1.0` 的存在,所以找不到。 因此为了更好地适配不同系统环境下,系统库的查找,我们可以通过 `add_extsources("pkgconfig::libusb-1.0")` 去让 xmake 改进查找逻辑,例如: ```lua package("libusb") add_extsources("pkgconfig::libusb-1.0") on_install(function (package) -- ... end) ``` 另外,我们也可以通过这个方式,改进查找 homebrew/pacman 等其他包管理器安装的包,例如:`add_extsources("pacman::libusb-1.0")`。 ## add\_deps * 添加包依赖 #### 函数原型 ::: tip API ```lua add_deps(deps: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | deps | 依赖包名称字符串或数组 | | ... | 可变参数,可传入多个依赖名称 | #### 用法说明 添加包依赖,用于指定包之间的依赖关系。 添加包依赖接口,通过配置包之间的依赖关系,我们能够在安装包的同时,自动安装它的所有依赖包。 另外,默认情况下,我们只要配置了依赖关系,cmake/autoconf 就能够自动找到所有依赖包的库和头文件。 当然,如果由于一些特殊原因,导致当前包的 cmake 脚本没能够正常找到依赖包,那么我们也可以通过 `{packagedeps = "xxx"}` 来强行打入依赖包信息。 例如: ```lua package("foo") add_deps("cmake", "bar") on_install(function (package) local configs = {} import("package.tools.cmake").install(package, configs) end) ``` foo 包是使用 CMakeLists.txt 维护的,它在安装过程中,依赖 bar 包,因此,xmake 会优先安装 bar,并且让 cmake.install 在调用 cmake 时候,自动找到 bar 安装后的库。 但是,如果 foo 的 CMakeLists.txt 还是无法自动找到 bar,那么我们可以修改成下面的配置,强制将 bar 的 includedirs/links 等信息通过 flags 的方式,传入 foo。 ```lua package("foo") add_deps("cmake", "bar") on_install(function (package) local configs = {} import("package.tools.cmake").install(package, configs, {packagedeps = "bar"}) end) ``` ## add\_components * 添加包组件 #### 函数原型 ::: tip API ```lua add_components(components: , ..., { deps = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | components | 组件名称字符串或数组 | | ... | 可变参数,可传入多个组件名称 | | deps | 组件依赖数组 | #### 用法说明 添加包组件,用于指定包的组件结构。 这是 2.7.3 新加的接口,用于支持包的组件化配置,详情见:[#2636](https://github.com/xmake-io/xmake/issues/2636)。 通过这个接口,我们可以配置当前包实际可以提供的组件列表。 ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") ``` 在用户端,我们可以通过下面的方式来使用包的特定组件。 ```lua add_requires("sfml") target("test") add_packages("sfml", {components = "graphics") ``` ::: tip 注意 除了配置可用的组件列表,我们还需要对每个组件进行详细配置,才能正常使用,因此,它通常和 `on_component` 接口配合使用。 ::: 一个关于包组件的配置和使用的完整例子见:[components example](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua) ## set\_base * 继承包配置 #### 函数原型 ::: tip API ```lua set_base(package: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | package | 要继承的基础包名称 | #### 用法说明 这是 2.6.4 新加的接口,我们可以通过它去继承一个已有的包的全部配置,然后在此基础上重写部分配置。 这通常在用户自己的项目中,修改 xmake-repo 官方仓库的内置包比较有用,比如:修复改 urls,修改版本列表,安装逻辑等等。 例如,修改内置 zlib 包的 url,切到自己的 zlib 源码地址。 ```lua package("myzlib") set_base("zlib") set_urls("https://github.com/madler/zlib.git") package_end() add_requires("myzlib", {system = false, alias = "zlib"}) target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` 我们也可以用来单纯添加一个别名包。 ```lua package("onetbb") set_base("tbb") ``` 我们可以通过 `add_requires("onetbb")` 集成安装 tbb 包,只是包名不同而已。 ## on\_load * 加载包配置 #### 函数原型 ::: tip API ```lua on_load(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 包加载脚本函数,参数为package | #### 用法说明 这是个可选的接口,如果要更加灵活的动态判断各种平台架构,针对性做设置,可以在这个里面完成,例如: ```lua on_load(function (package) local bitwidth = package:config("bitwidth") or "8" package:add("links", "pcre" .. (bitwidth ~= "8" and bitwidth or "")) if not package:config("shared") then package:add("defines", "PCRE_STATIC") end end) ``` pcre包需要做一些针对bitwidth的判断,才能确定对外输出的链接库名字,还需要针对动态库增加一些defines导出,这个时候在on\_load里面设置,就更加灵活了。 ## on\_fetch * 从系统中查找库 #### 函数原型 ::: tip API ```lua on_fetch(platforms: , ..., script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | platforms | 平台过滤字符串或数组,可选 | | ... | 可变参数,可传入多个平台过滤器 | | script | 查找脚本函数,参数为package和opt | #### 用法说明 这是个可选配置,2.5.2 之后,如果不同系统下安装的系统库,仅仅只是包名不同,那么使用 `add_extsources` 改进系统库查找已经足够,简单方便。 但是如果有些安装到系统的包,位置更加复杂,想要找到它们,也许需要一些额外的脚本才能实现,例如:windows 下注册表的访问去查找包等等,这个时候,我们就可以通过 `on_fetch` 完全定制化查找系统库逻辑。 还是以 libusb 为例,我们不用 `add_extsources`,可以使用下面的方式,实现相同的效果,当然,我们可以在里面做更多的事情。 ``` package("libusb") on_fetch("linux", function(package, opt) if opt.system then return find_package("pkgconfig::libusb-1.0") end end) ``` ## on\_check * 检测包是否支持当前平台 #### 函数原型 ::: tip API ```lua on_check(platforms: , ..., script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | platforms | 平台过滤字符串或数组,可选 | | ... | 可变参数,可传入多个平台过滤器 | | script | 检测脚本函数,参数为package | #### 用法说明 有时候,单纯用 `on_install("windows", "android", function () end)` 无法很好的限制包对当前平台的支持力度。 例如,同样都是在 windows 上使用 msvc 编译,但是它仅仅只支持使用 vs2022 工具链。那么我们无法简单的去通过禁用 windows 平台,来限制包的安装。 因为每个用户的编译工具链环境都可能是不同的。这个时候,我们可以通过配置 `on_check` 去做更细致的检测,来判断包是否支持当前的工具链环境。 如果包不被支持,那么它会在包被下载安装前,更早的提示用户,也可以在 xmake-repo 的 ci 上,规避掉一些不支持的 ci job 测试。 例如,下面的配置,就可以判断当前的 msvc 是否提供了对应的 vs sdk 版本,如果版本不满足,那么这个包就无法被编译安装,用户会看到更加可读的不支持的错误提示。 ```lua package("test") on_check("windows", function (package) import("core.tool.toolchain") import("core.base.semver") local msvc = toolchain.load("msvc", {plat = package:plat(), arch = package:arch()}) if msvc then local vs_sdkver = msvc:config("vs_sdkver") assert(vs_sdkver and semver.match(vs_sdkver):gt("10.0.19041"), "package(cglm): need vs_sdkver > 10.0.19041.0") end end) ``` 例如,我们也可以用它来判断,当前编译器对 c++20 的支持力度,如果不支持 c++20 才有的 std::input\_iterator。那么这个包就没必要继续下载编译安装。 用户会看到 `Require at least C++20.` 的错误,来提示用户取升级自己的编译器。 ```lua package("test") on_check(function (package) assert(package:check_cxxsnippets({test = [[ #include #include struct SimpleInputIterator { using difference_type = std::ptrdiff_t; using value_type = int; int operator*() const; SimpleInputIterator& operator++(); void operator++(int) { ++*this; } }; static_assert(std::input_iterator); ]]}, {configs = {languages = "c++20"}}), "Require at least C++20.") end) ``` ## on\_install * 安装包 #### 函数原型 ::: tip API ```lua on_install(platforms: , ..., script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | platforms | 平台过滤字符串或数组,可选 | | ... | 可变参数,可传入多个平台过滤器 | | script | 安装脚本函数,参数为package | #### 用法说明 这个接口主要用于添加安装脚本,前面的字符串参数用于设置支持的平台,像`on_load`, `on_test`等其他脚本域也是同样支持的。 ### 平台过滤 完整的过滤语法如下:`plat|arch1,arch2@host|arch1,arch2` 看上去非常的复杂,其实很简单,其中每个阶段都是可选的,可部分省略,对应:`编译平台|编译架构@主机平台|主机架构` 如果不设置任何平台过滤条件,那么默认全平台支持,里面的脚本对所有平台生效,例如: ```lua on_install(function (package) -- TODO end) ``` 如果安装脚本对特定平台生效,那么直接指定对应的编译平台,可以同时指定多个: ```lua on_install("linux", "macosx", function (package) -- TODO end) ``` 如果还要细分到指定架构才能生效,可以这么写: ```lua on_install("linux|x86_64", "iphoneos|arm64", function (package) -- TODO end) ``` 如果还要限制执行的主机环境平台和架构,可以在后面追加`@host|arch`,例如: ```lua on_install("mingw@windows", function (package) -- TODO end) ``` 意思就是仅对windows下编译mingw平台生效。 我们也可以不指定比那一平台和架构,仅设置主机平台和架构,这通常用于描述一些跟编译工具相关的依赖包,只能在主机环境运行。 例如,我们编译的包,依赖了cmake,需要添加cmake的包描述,那么里面编译安装环境,只能是主机平台: ```lua on_install("@windows", "@linux", "@macosx", function (package) -- TODO end) ``` 其他一些例子: ```lua -- `@linux` -- `@linux|x86_64` -- `@macosx,linux` -- `android@macosx,linux` -- `android|armeabi-v7a@macosx,linux` -- `android|armeabi-v7a@macosx,linux|x86_64` -- `android|armeabi-v7a@linux|x86_64` ``` 在 2.8.7 中,我们改进了模式匹配支持,新增排除指定平台和架构,例如: ``` !plat|!arch@!subhost|!subarch ``` ```lua @!linux @!linux|x86_64 @!macosx,!linux !android@macosx,!linux android|!armeabi-v7a@macosx,!linux android|armeabi-v7a,!iphoneos@macosx,!linux|x86_64 !android|armeabi-v7a@!linux|!x86_64 !linux|* ``` 同时,还提供了一个内置的 `native` 架构,用于匹配当前平台的本地架构,主要用于指定或者排除交叉编译平台。 ```lua on_install("macosx|native", ...) ``` 上面的配置,如果在 macOS x86\_64 的设备上,它仅仅只会匹配 `xmake f -a x86_64` 的本地架构编译。 如果是 `xmake f -a arm64` 交叉编译,就不会被匹配到。 同理,如果只想匹配交叉编译,可以使用 `macosx|!native` 进行取反排除就行了。 2.9.1 版本,我们继续对它做了改进,增加了条件逻辑判断的支持: 例如: ```lua on_install("!wasm|!arm* and !cross|!arm*", function (package) end) ``` 来表述排除 wasm 和 cross 平台之外的 arm 架构。 并且,它也支持通过 `()` 描述的嵌套逻辑,`a and b or (a and (c or d))`。 ### 编译工具 我们内置了一些安装常用编译工具脚本,用于针对不同源码依赖的构建工具链,进行方便的构架支持,例如:autoconf, cmake, meson等, ### xmake 如果是基于xmake的依赖包,那么集成起来就非常简单了,xmake对其做了非常好的内置集成支持,可以直接对其进行跨平台编译支持,一般情况下只需要: ```lua on_install(function (package) import("package.tools.xmake").install(package) end) ``` 如果要传递一些特有的编译配置参数: ```lua on_install(function (package) import("package.tools.xmake").install(package, {"--xxx=y"}) end) ``` ### cmake 如果是基于cmake的包,集成起来也很简答,通常也只需要设置一些配置参数即可,不过还需要先添加上cmake的依赖才行: ```lua add_deps("cmake") on_install(function (package) import("package.tools.cmake").install(package, {"-Dxxx=ON"}) end) ``` ### autoconf 如果是基于autoconf的包,集成方式跟cmake类似,只是传递的配置参数不同而已,不过通常情况下,unix系统都内置了autoconf系列工具,所以不加相关依赖也没事。 ```lua on_install(function (package) import("package.tools.autoconf").install(package, {"--enable-shared=no"}) end) ``` 不过,有些源码包用系统内置的autoconf可能不能完全满足,那么可以加上autoconf系列依赖,对其进行构建: ```lua add_deps("autoconf", "automake", "libtool", "pkg-config") on_install(function (package) import("package.tools.autoconf").install(package, {"--enable-shared=no"}) end) ``` ### meson 如果是meson,还需要加上ninja的依赖来执行构建才行。 ```lua add_deps("meson", "ninja") on_install(function (package) import("package.tools.meson").install(package, {"-Dxxx=ON"}) end) ``` ## on\_test * 测试包 #### 函数原型 ::: tip API ```lua on_test(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 测试脚本函数,参数为package | #### 用法说明 安装后,需要设置对应的测试脚本,执行一些测试,确保安装包的可靠性,如果测试不通过,则会撤销整个安装包。 ```lua on_test(function (package) assert(package:has_cfuncs("inflate", {includes = "zlib.h"})) end) ``` 上面的脚本调用包内置的`has_cfuncs`接口,检测安装后的包是否存在zlib.h头文件,以及库和头文件里面是否存在`inflate`这个接口函数。 xmake会去尝试编译链接来做测试,`has_cfuncs`用于检测c函数,而`has_cxxfuncs`则可以检测c++库函数。 而includes里面可以设置多个头文件,例如:`includes = {"xxx.h", "yyy.h"}` 我们还可以传递一些自己的编译参数进去检测,例如: ```lua on_test(function (package) assert(package:has_cxxfuncs("func1", {includes = "xxx.h", configs = {defines = "c++14", cxflags = "-Dxxx"}})) end) ``` 我们也可以通过`check_csnippets`和`check_cxxsnippets`检测一个代码片段: ```lua on_test(function (package) assert(package:check_cxxsnippets({test = [[ #include #include #include #include using namespace boost::algorithm; using namespace std; static void test() { string str("a,b"); vector strVec; split(strVec, str, is_any_of(",")); assert(strVec.size()==2); assert(strVec[0]=="a"); assert(strVec[1]=="b"); } ]]}, {configs = {languages = "c++14"}})) end) ``` 如果是可执行包,也可以通过尝试运行来检测: ```lua on_test(function (package) os.run("xxx --help") end) ``` 如果运行失败,那么测试不会通过。 ## on\_download * 自定义下载包 #### 函数原型 ::: tip API ```lua on_download(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 下载脚本函数,参数为package和opt | #### 用法说明 自定义下载包,用于指定包的下载方式。 自定义包的下载逻辑,这是 2.6.4 新加的接口,通常用不到,使用 Xmake 的内置下载就足够了。 如果用户自建私有仓库,对包的下载有更复杂的鉴权机制,特殊处理逻辑,那么可以重写内部的下载逻辑来实现。 ```lua on_download(function (package, opt) local url = opt.url local sourcedir = opt.sourcedir -- download url to the current directory -- and extract it's source code to sourcedir -- ... end) ``` opt 参数里面,可以获取到下载包的目的源码目录 `opt.sourcedir`,我们只需要从 `opt.url` 获取到包地址,下载下来就可以了。 然后,根据需要,添加一些自定义的处理逻辑。另外,自己可以添加下载缓存处理等等。 下面是一个自定义下载 tar.gz 文件,并且实现缓存和源文件目录解压的例子,可以参考下: ```lua package("zlib") add_urls("https://github.com/madler/zlib/archive/$(version).tar.gz") add_versions("v1.2.10", "42cd7b2bdaf1c4570e0877e61f2fdc0bce8019492431d054d3d86925e5058dc5") on_download(function (package, opt) import("net.http") import("utils.archive") local url = opt.url local sourcedir = opt.sourcedir local packagefile = path.filename(url) local sourcehash = package:sourcehash(opt.url_alias) local cached = true if not os.isfile(packagefile) or sourcehash ~= hash.sha256(packagefile) then cached = false -- attempt to remove package file first os.tryrm(packagefile) http.download(url, packagefile) -- check hash if sourcehash and sourcehash ~= hash.sha256(packagefile) then raise("unmatched checksum, current hash(%s) != original hash(%s)", hash.sha256(packagefile):sub(1, 8), sourcehash:sub(1, 8)) end end -- extract package file local sourcedir_tmp = sourcedir .. ".tmp" os.rm(sourcedir_tmp) if archive.extract(packagefile, sourcedir_tmp) then os.rm(sourcedir) os.mv(sourcedir_tmp, sourcedir) else -- if it is not archive file, we need only create empty source file and use package:originfile() os.tryrm(sourcedir) os.mkdir(sourcedir) end -- save original file path package:originfile_set(path.absolute(packagefile)) end) ``` 自定义下载需要用户完全自己控制下载逻辑,会比较复杂,除非必要,不推荐这么做。 如果仅仅只是想增加自定义 http headers 去获取下载授权,可以使用 [设置包下载的 http headers](/zh/api/description/builtin-policies#package-download-http-headers)。 ## on\_component * 配置包组件 #### 函数原型 ::: tip API ```lua on_component(component: , script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | component | 组件名称字符串,可选(如果不提供,则应用于所有组件) | | script | 组件配置脚本函数,参数为package和component | #### 用法说明 这是 2.7.3 新加的接口,用于支持包的组件化配置,详情见:[#2636](https://github.com/xmake-io/xmake/issues/2636)。 通过这个接口,我们可以配置当前包,指定组件的详细信息,比如组件的链接,依赖等等。 ### 配置组件链接信息 ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-graphics" .. e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("links", "freetype") component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) on_component("window", function (package, component) local e = package:config("shared") and "" or "-s" component:add("links", "sfml-window" .. e) if package:is_plat("windows", "mingw") and not package:config("shared") then component:add("syslinks", "opengl32", "gdi32", "user32", "advapi32") end end) ... ``` 在用户端,我们可以通过下面的方式来使用包的特定组件。 ```lua add_requires("sfml") target("test") add_packages("sfml", {components = "graphics") ``` ::: tip 注意 除了配置组件信息,我们还需要配置可用的组件列表,才能正常使用,因此,它通常和 `add_components` 接口配合使用。 ::: 一个关于包组件的配置和使用的完整例子见:[components example](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/components/xmake.lua) ### 配置组件的编译信息 我们不仅可以配置每个组件的链接信息,还有 includedirs, defines 等等编译信息,我们也可以对每个组件单独配置。 ```lua package("sfml") on_component("graphics", function (package, component) package:add("defines", "TEST") end) ``` ### 配置组件依赖 ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) component:add("deps", "window", "system") end) ``` 上面的配置,告诉包,我们的 graphics 组件还会额外依赖 `window` 和 `system` 两个组件。 因此,在用户端,我们对 graphics 的组件使用,可以从 ```lua add_packages("sfml", {components = {"graphics", "window", "system"}) ``` 简化为: ```lua add_packages("sfml", {components = "graphics") ``` 因为,只要我们开启了 graphics 组件,它也会自动启用依赖的 window 和 system 组件。 另外,我们也可以通过 `add_components("graphics", {deps = {"window", "system"}})` 来配置组件依赖关系。 ### 从系统库中查找组件 我们知道,在包配置中,配置 `add_extsources` 可以改进包在系统中的查找,比如从 apt/pacman 等系统包管理器中找库。 当然,我们也可以让每个组件也能通过 `extsources` 配置,去优先从系统库中找到它们。 例如,sfml 包,它在 homebrew 中其实也是组件化的,我们完全可以让包从系统库中,找到对应的每个组件,而不需要每次源码安装它们。 ```sh $ ls -l /usr/local/opt/sfml/lib/pkgconfig -r--r--r-- 1 ruki admin 317 10 19 17:52 sfml-all.pc -r--r--r-- 1 ruki admin 534 10 19 17:52 sfml-audio.pc -r--r--r-- 1 ruki admin 609 10 19 17:52 sfml-graphics.pc -r--r--r-- 1 ruki admin 327 10 19 17:52 sfml-network.pc -r--r--r-- 1 ruki admin 302 10 19 17:52 sfml-system.pc -r--r--r-- 1 ruki admin 562 10 19 17:52 sfml-window.pc ``` 我们只需要,对每个组件配置它的 extsources: ```lua if is_plat("macosx") then add_extsources("brew::sfml/sfml-all") end on_component("graphics", function (package, component) -- ... component:add("extsources", "brew::sfml/sfml-graphics") end) ``` ### 默认的全局组件配置 除了通过指定组件名的方式,配置特定组件,如果我们没有指定组件名,默认就是全局配置所有组件。 ```lua package("sfml") on_component(function (package, component) -- configure all components end) ``` 当然,我们也可以通过下面的方式,指定配置 graphics 组件,剩下的组件通过默认的全局配置接口进行配置: ```lua package("sfml") add_components("graphics") add_components("audio", "network", "window") add_components("system") on_component("graphics", function (package, component) -- configure graphics end) on_component(function (package, component) -- component audio, network, window, system end) ``` --- --- url: /zh/api/scripts/package-instance.md --- # 包实例 {#package-instance} 此页面描述了 [包依赖管理](/zh/api/description/package-dependencies) 的 `on_load()`、`on_install()` 或 `on_test()` 等函数的 `package` 接口 ## package:name * 获取包的名字 #### 函数原型 ::: tip API ```lua package:name() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:get * 获取包在描述域的配置值 #### 函数原型 ::: tip API ```lua package:get(key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | #### 用法说明 任何在描述域的 `set_xxx` 和 `add_xxx` 配置值都可以通过这个接口获取到。 ```lua -- get the dependencies package:get("deps") -- get the links package:get("links") -- get the defined macros package:get("defines") ``` ## package:set * 设置包的配置值 #### 函数原型 ::: tip API ```lua package:set(key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | | value | 配置值 | #### 用法说明 如果你想添加值可以用 [package:add](#package-add)。 ```lua -- set the dependencies package:set("deps", "python") -- set the links package:set("links", "sdl2") -- set the defined macros package:set("defines", "SDL_MAIN_HANDLED") ``` ::: tip 注意 任何脚本域下对 `package:set("xxx", ...)` 的配置,都是完全跟描述域的 `set_xxx` 接口保持一致的,具体参数说明,可以直接参考描述域下对应的 `set_xxx` 接口说明。 例如: * 描述域:`set_urls("https://github.com/madler/zlib/archive/$(version).tar.gz")` * 脚本域:`package:set("urls", "https://github.com/madler/zlib/archive/$(version).tar.gz")` ::: ## package:add * 按名称添加到包的值 #### 函数原型 ::: tip API ```lua package:add(key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | | value | 要添加的值 | #### 用法说明 ```lua -- add dependencies package:add("deps", "python") -- add links package:add("links", "sdl2") -- add defined macros package:add("defines", "SDL_MAIN_HANDLED") ``` ::: tip 注意 任何脚本域下对 `package:add("xxx", ...)` 的配置,都是完全跟描述域的 `add_xxx` 接口保持一致的,具体参数说明,可以直接参考描述域下对应的 `add_xxx` 接口说明。 例如: * 描述域:`add_deps("zlib", {configs = {shared = true}})` * 脚本域:`package:add("deps", "zlib", {configs = {shared = true}})` ::: ## package:license * 获取包的许可证(同`package:get("license")`) #### 函数原型 ::: tip API ```lua package:license() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:description * 获取包的描述(同`package:get("description")`) #### 函数原型 ::: tip API ```lua package:description() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:plat * 获取包的平台。 可以是以下任何一种: #### 函数原型 ::: tip API ```lua package:plat() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 * windows * linux * macosx * android * iphoneos * watchos * mingw * cygwin * bsd 如果包是二进制的,则会返回 [`os.host`](/zh/api/scripts/builtin-modules/os#os-host) 的值 ## package:arch * 获取包的架构(例如 x86、x64、x86\_64) #### 函数原型 ::: tip API ```lua package:arch() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 如果包是二进制的,则返回 [`os.arch`](/zh/api/scripts/builtin-modules/os#os-arch) ## package:targetos * 获取包的目标操作系统。 #### 函数原型 ::: tip API ```lua package:targetos() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 可以具有与 [package:plat](#package-plat) 相同的值 ## package:targetarch * 获取包的目标架构。 #### 函数原型 ::: tip API ```lua package:targetarch() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 可以具有与 [package:arch](#package-arch) 相同的值 ## package:is\_plat * 当前平台是否是给定平台之一 #### 函数原型 ::: tip API ```lua package:is_plat(plat: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | plat | 平台名称 | #### 用法说明 ```lua -- Is the current platform android? package:is_plat("android") -- Is the current platform windows, linux or macosx? package:is_plat("windows", "linux", "macosx") ``` ## package:is\_arch * 当前架构是否是给定架构之一 #### 函数原型 ::: tip API ```lua package:is_arch(arch: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | arch | 架构名称 | #### 用法说明 ```lua -- Is the current architecture x86 package:is_arch("x86") -- Is the current architecture x64 or x86_64 package:is_arch("x64", "x86_64") ``` ## package:is\_targetos * 当前目标操作系统是否是给定操作系统之一 #### 函数原型 ::: tip API ```lua package:is_targetos() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- Is the currently targeted OS windows? package:is_targetos("windows") -- Is the currently targeted OS android or iphoneos? package:is_targetos("android", "iphoneos") ``` ## package:is\_targetarch * 当前目标架构是否是给定架构之一 #### 函数原型 ::: tip API ```lua package:is_targetarch() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- Is the currently targeted architecture x86 package:is_targetarch("x86") -- Is the currently targeted architecture x64 or x86_64 package:is_targetarch("x64", "x86_64") ``` ## package:alias * 获取包的别名 #### 函数原型 ::: tip API ```lua package:alias() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 如果用户像这样设置别名: ```lua add_requires("libsdl", {alias = "sdl"}) ``` 那么这个别名可以通过这个接口获取到: ```lua -- returns "sdl" package:alias() ``` ## package:urls * 获取包的 urls 列表 #### 函数原型 ::: tip API ```lua package:urls() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 如果我们设置了如下 URLs ```lua add_urls("https://example.com/library-$(version).zip") -- or so set_urls("https://example.com/library-$(version).zip") ``` 那么我们可以通过下面的接口来获取 ```lua -- returns the table {"https://example.com/library-$(version).zip"} package:urls() ``` ## package:dep * 通过名称获取包的依赖项。 该名称需要是包的依赖项。 #### 函数原型 ::: tip API ```lua package:dep(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 依赖名称 | #### 用法说明 ```lua local python = package:dep("python") -- returns "python" python:name() ``` ## package:deps * 获取包的所有依赖项 #### 函数原型 ::: tip API ```lua package:deps() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- prints the names of all dependencies for _,dep in pairs(package:deps()) do print(dep:name()) end ``` ## package:sourcehash * 获取 URL 别名的 sha256 校验和 #### 函数原型 ::: tip API ```lua package:sourcehash() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 如果校验和是这样提供的: ```lua add_urls("https://example.com/library-$(version).zip", {alias = "example"}) add_versions("example:2.4.1", "29f9983cc7196e882c4bc3d23d7492f9c47574c7cf658afafe7d00c185429941") ``` 您可以像这样获取它: ```lua -- returns "29f9983cc7196e882c4bc3d23d7492f9c47574c7cf658afafe7d00c185429941" package:sourcehash("example") -- or so package:sourcehash(package:url_alias(package:urls()[1])) ``` ## package:kind * 获取包的类型。 可以是以下任何一种: #### 函数原型 ::: tip API ```lua package:kind() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 * binary * toolchain (is also binary) * library (default) * template [#2138](https://github.com/xmake-io/xmake/issues/2138) * headeronly ## package:is\_binary * 包是否为二进制类型 #### 函数原型 ::: tip API ```lua package:is_binary() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:is\_toolchain * 报是否为工具链类型 #### 函数原型 ::: tip API ```lua package:is_toolchain() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:is\_library * 包是否为库类型 #### 函数原型 ::: tip API ```lua package:is_library() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:is\_toplevel \-- 包是否在用户 xmake.lua 里面通过 add\_requires 直接引用 ## package:is\_thirdparty * 包是否由第三方包管理器提供(例如 brew、conan、vcpkg) #### 函数原型 ::: tip API ```lua package:is_thirdparty() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:is\_debug * 包是否以调试模式构建 #### 函数原型 ::: tip API ```lua package:is_debug() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 同`package:config("debug")` ## package:is\_supported * 当前平台和架构是否支持该包 #### 函数原型 ::: tip API ```lua package:is_supported() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:debug * 包是否使用调试模式构建 #### 函数原型 ::: tip API ```lua package:debug() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 不推荐使用:使用 [`package:is_debug`](#package-is_debug) 代替。 ## package:is\_cross * 包是否正在交叉编译 #### 函数原型 ::: tip API ```lua package:is_cross() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:cachedir * 获取包的缓存目录 #### 函数原型 ::: tip API ```lua package:cachedir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:installdir * 获取包的安装目录。 也可用于获取子目录。 如果给定的目录树不存在,它将被创建。 #### 函数原型 ::: tip API ```lua package:installdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- returns the installation directory package:installdir() -- returns the subdirectory include inside the installation directory package:installdir("include") -- returns the subdirectory include/files package:installdir("include", "files") ``` ## package:scriptdir * 获取包的xmake.lua所在目录 #### 函数原型 ::: tip API ```lua package:scriptdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:envs * 获取包导出的环境变量 #### 函数原型 ::: tip API ```lua package:envs() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:getenv * 获取给定的环境变量 #### 函数原型 ::: tip API ```lua package:getenv() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- returns a table package:getenv("PATH") ``` ## package:setenv * 设置给定的环境变量。 覆盖变量 #### 函数原型 ::: tip API ```lua package:setenv() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- sets PATH to {"bin", "lib"} package:setenv("PATH", "bin", "lib") ``` ## package:addenv * 将给定的值添加到环境变量 #### 函数原型 ::: tip API ```lua package:addenv() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- adds "bin" and "lib" to PATH package:addenv("PATH", "bin", "lib") ``` ## package:versions * 获取包的所有版本列表。 #### 函数原型 ::: tip API ```lua package:versions() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:version * 获取包的版本 #### 函数原型 ::: tip API ```lua package:version() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 它会返回一个语义版本对象,便于做版本之间的判断。 ```lua local version = package:version() -- get the major version version:major() -- get the minor version version:minor() -- get the patch version version:patch() ``` ## package:version\_str * 以字符串形式获取包的版本 #### 函数原型 ::: tip API ```lua package:version_str() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## package:config * 获取包的给定配置值 #### 函数原型 ::: tip API ```lua package:config() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- if configurations are set like so add_require("example", {configs = {enable_x = true, value_y = 6}}) -- these values can be retrieved like so -- returns true package:config("enable_x") -- returns 6 package:config("value_y") ``` ## package:config\_set * 设置包的给定配置值 #### 函数原型 ::: tip API ```lua package:config_set() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua package:config_set("enable_x", true) package:config_set("value_y", 6) ``` ## package:configs * 获取包的所有配置 #### 函数原型 ::: tip API ```lua package:configs() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- returns a table with the configuration names as keys and their values as values local configs = package:configs() local enable_x = configs["enable_x"] local value_y = configs["value_y"] ``` ## package:buildhash * 获取包的构建哈希 #### 函数原型 ::: tip API ```lua package:buildhash() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 它确保每个包,不同的配置安装到唯一的路径下,相互之间不冲突。 ## package:patches * 获取当前版本的所有补丁 #### 函数原型 ::: tip API ```lua package:patches() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- returns a table with all patches local patches = package:patches() -- each element contains the keys "url" and "sha256" local url = patches[1]["url"] local sha256 = patches[1]["sha256"] ``` ## package:has\_cfuncs * 检测包是否具有给定的 C 函数 #### 函数原型 ::: tip API ```lua package:has_cfuncs() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:has_cfuncs("foo")) -- you can also add configs assert(package:has_cfuncs("bar", {includes = "foo_bar.h"})) assert(package:has_cfuncs("blob", {includes = "blob.h", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_cfuncs("bla", {configs = {languages = "c99"}})) end) ``` ## package:has\_cxxfuncs * 检测包是否具有给定的 C++ 函数 #### 函数原型 ::: tip API ```lua package:has_cxxfuncs(funcs: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | funcs | 函数名或函数名列表 | #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:has_cxxfuncs("foo")) -- you can also add configs assert(package:has_cxxfuncs("bar", {includes = "foo_bar.hpp"})) assert(package:has_cxxfuncs("blob", {includes = "blob.hpp", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_cxxfuncs("bla", {configs = {languages = "cxx17"}})) end) ``` ## package:has\_ctypes * 检测包是否具有给定的 C 类型 #### 函数原型 ::: tip API ```lua package:has_ctypes(types: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 类型名或类型名列表 | #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:has_ctypes("foo")) -- you can also add configs assert(package:has_ctypes("bar", {includes = "foo_bar.h"})) assert(package:has_ctypes("blob", {includes = "blob.h", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_ctypes("bla", {configs = {languages = "c99"}})) end) ``` ## package:has\_cxxtypes * 检测包是否具有给定的 C++ 类型 #### 函数原型 ::: tip API ```lua package:has_cxxtypes(types: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 类型名或类型名列表 | #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:has_cxxtypes("foo")) -- you can also add configs assert(package:has_cxxtypes("bar", {includes = "foo_bar.hpp"})) assert(package:has_cxxtypes("blob", {includes = "blob.hpp", configs = {defines = "USE_BLOB"}})) -- you can even set the language assert(package:has_cxxtypes("bla", {configs = {languages = "cxx17"}})) end) ``` ## package:has\_cincludes * 检测包是否具有给定的 C 头文件 #### 函数原型 ::: tip API ```lua package:has_cincludes(includes: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 头文件或头文件列表 | #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:has_cincludes("foo.h")) end) ``` ## package:has\_cxxincludes * 检测包是否具有给定的 C++ 头文件 #### 函数原型 ::: tip API ```lua package:has_cxxincludes(includes: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 头文件或头文件列表 | #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:has_cxxincludes("foo.hpp")) end) ``` ## package:check\_csnippets * 检测是否可以编译和链接给定的 C 代码片段 #### 函数原型 ::: tip API ```lua package:check_csnippets(snippets: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | snippets | 代码片段或代码片段列表 | #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:check_csnippets({test = [[ #define USE_BLOB #include void test(int argc, char** argv) { foo bar; printf("%s", bar.blob); } ]]}, {configs = {languages = "c99"}, includes = "foo.h"})) end) ``` ## package:check\_cxxsnippets * 检测是否可以编译和链接给定的 C++ 代码片段 #### 函数原型 ::: tip API ```lua package:check_cxxsnippets(snippets: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | snippets | 代码片段或代码片段列表 | #### 用法说明 这应该在 `on_test` 中使用,如下所示: ```lua on_test(function (package) assert(package:check_cxxsnippets({test = [[ #define USE_BLOB #include void test(int argc, char** argv) { foo bar(); std::cout << bar.blob; } ]]}, {configs = {languages = "cxx11"}, includes = "foo.hpp"})) end) ``` ## package:check\_fcsnippets * 检测是否可以编译和链接给定的 Fortran 代码片段 #### 函数原型 ::: tip API ```lua package:check_fcsnippets() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 用法如上 --- --- url: /zh/api/scripts/native-modules.md --- # 原生模块 {#native-modules} 我们知道,在 xmake 中,可以通过 import 接口去导入一些 lua 模块在脚本域中使用,但是如果一些模块的操作比较耗时,那么 lua 实现并不是理想的选择。 因此,新版本中,我们新增了 native lua 模块的支持,可以通过 native 实现,来达到提速优化的效果,并且模块导入和使用,还是跟 lua 模块一样简单。 使用原生模块时,xmake 会进行两段编译,先会自动编译原生模块,后将模块导入 lua 作为库或二进制,而对于用户,仅仅只需要调用 import 导入即可。 ## 定义动态库模块 {#define-dynamic-module} 动态库模块的好处是,不仅仅通过 native 实现了性能加速,另外避免了每次调用额外的子进程创建,因此更加的轻量,速度进一步得到提升。 我们可以先定义一个动态库模块,里面完全支持 lua 的所有 c API,因此我们也可以将一些第三方的开源 lua native 模块直接引入进来使用。 这里我们也有一个完整的导入 lua-cjson 模块的例子可以参考:[native\_module\_cjson](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/native_module_cjson) 首先,我们先实现 shared 的 native 代码,所以接口通过 lua API 导出。 ```c++ [./modules/foo/foo.c] #include static int c_add(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a + b); return 1; } static int c_sub(lua_State* lua) { int a = lua_tointeger(lua, 1); int b = lua_tointeger(lua, 2); lua_pushinteger(lua, a - b); return 1; } int luaopen(foo, lua_State* lua) { // 收集add和sub static const luaL_Reg funcs[] = { {"add", c_add}, {"sub", c_sub}, {NULL, NULL} }; lua_newtable(lua); // 传递函数列表 luaL_setfuncs(lua, funcs, 0); return 1; } ``` 注意到这里,我们 include 了一个 `xmi.h` 的接口头文件,其实我们也可以直接引入 `lua.h`,`luaconf.h`,效果是一样的,但是会提供更好的跨平台性,内部会自动处理 lua/luajit还有版本间的差异。 然后,我们配置 `add_rules("modules.shared")` 作为 shared native 模块来编译,不需要引入任何其他依赖。 甚至连 lua 的依赖也不需要引入,因为 xmake 主程序已经对其导出了所有的 lua 接口,可直接使用,所以整个模块是非常轻量的。 ```lua [./modules/foo/xmake.lua] add_rules("mode.debug", "mode.release") target("foo") -- 指定目标为库lua模块 add_rules("module.shared") add_files("foo.c") ``` ## 定义二进制模块 {#define-binary-module} 出了动态库模块,我们还提供了另外一种二进制模块的导入。它其实就是一个可执行文件,每次调用模块接口,都会去调用一次子进程。 那它有什么好处呢,尽管它没有动态库模块那么高效,但是它的模块实现更加的简单,不需要调用 lua API,仅仅只需要处理参数数据,通过 stdout 去输出返回值即可。 另外,相比二进制分发,它是通过源码分发的,因此也解决了跨平台的问题。 具体是使用动态库模块,还是二进制模块,具体看自己的需求,如果想要实现简单,可以考虑二进制模块,如果想要高效,就用动态库模块。 另外,如果需要通过并行执行来提速,也可以使用二进制模块。 ```c++ [./modules/bar/bar.cpp] #include #include #include int main(int argc, char** argv) { int a = atoi(argv[1]); int b = atoi(argv[2]); printf("%d", a + b); return 0; } ``` ```lua [./modules/bar/xmake.lua] add_rules("mode.debug", "mode.release") target("add") -- 指定目标为二进制lua模块 add_rules("module.binary") add_files("bar.cpp") ``` ## 导入原生模块 {#import-native-module} 对于模块导入,我们仅仅需要调用 import,跟导入 lua 模块的用法完全一致。 ```lua [./xmake.lua] add_rules("mode.debug", "mode.release") -- 添加./modules目录内原生模块 add_moduledirs("modules") target("test") set_kind("phony") on_load(function(target) import("foo", {always_build = true}) import("bar") print("foo: 1 + 1 = %s", foo.add(1, 1)) print("foo: 1 - 1 = %s", foo.sub(1, 1)) print("bar: 1 + 1 = %s", bar.add(1, 1)) end) ``` 由于插件模块的构建是跟主工程完全独立的,因此,native 模块只会被构建一次,如果想要触发增量的插件编译,需要配置上 `always_build = true`,这样,xmake 就会每次检测插件代码是否有改动,如果有改动,会自动增量构建插件。 首次执行效果如下: ```sh ruki-2:native_module ruki$ xmake [ 50%]: cache compiling.release src/foo.c [ 50%]: cache compiling.release src/bar.c [ 75%]: linking.release libmodule_foo.dylib [ 75%]: linking.release module_bar [100%]: build ok, spent 1.296s foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` 第二次执行,就不会再构建插件,可以直接使用模块: ```sh ruki-2:native_module ruki$ xmake foo: 1 + 1 = 2 foo: 1 - 1 = 0 bar: 1 + 1 = 2 [100%]: build ok, spent 0.447s ``` ## 作为 codegen 来使用 {#auto-codegen} 通过新的 native 模块特性,我们也可以用来实现 auto-codegen,然后根据自动生成的代码,继续执行后续编译流程。 这里也有完整的例子可以参考:[autogen\_shared\_module](https://github.com/xmake-io/xmake/tree/master/tests/projects/other/autogen/autogen_shared_module)。 --- --- url: /zh/guide/project-configuration/namespace-isolation.md --- # 命名空间隔离 {#namespace-isolation} 如果用户维护了两个独立的子工程,它们内部存在一些同名的 目标,选项,以及规则名,那么通过 includes 整合到一个工程中时,可能会存在命名冲突问题导致编译出错。 为了规避这么问题,Xmake 提供了命名空间的特性,来隔离不同的工程到到独立的命名空间中去,使其既可以独立构建,也可以合并且保证不冲突,例如: ```lua [xmake.lua] add_rules("mode.debug", "mode.release") add_defines("ROOT") namespace("ns1", function () includes("foo") end) target("foo") set_kind("binary") add_deps("ns1::foo") add_files("src/main.cpp") add_defines("TEST") ``` ```lua [foo/xmake.lua] add_defines("NS1_ROOT") target("foo") set_kind("static") add_files("src/foo.cpp") add_defines("FOO") ``` 上述配置,foo 是一个独立的工程目录,可以单独构建,它是一个库工程。 但是我们又可以通过 `includes("foo")` 引入到另外一个工程中使用,那个工程也有一个同名的 foo 目标。 由于命名空间的隔离,它们之间并不会导致冲突,我们可以通过 `ns1::foo` 去访问 foo 子工程中的目标。另外命名空间中的根作用域配置也不会互相影响。 更多关于命名空间的使用说明,请查看完整文档:[命名空间 API 手册](/zh/api/description/global-interfaces#namespace)。 --- --- url: /zh/guide/package-management/using-packages-in-cmake.md --- # 在 CMake 中使用包 {#using-package-in-cmake} 我们新增了一个独立项目 [xrepo-cmake](https://github.com/xmake-io/xrepo-cmake)。 它是一个基于 Xrepo/Xmake 的 C/C++ 包管理器的 CMake 包装器。 这允许使用 CMake 构建您的项目,同时用 Xrepo 管理依赖包。该项目部分灵感来自 [cmake-conan](https://github.com/conan-io/cmake-conan)。 此项目的示例用例: * 想要使用 Xrepo 管理包的现有 CMake 项目。 * 必须使用 CMake,但想使用 Xrepo 管理的新项目包。 ## APIs ### xrepo\_package [xrepo.cmake](https://github.com/xmake-io/xrepo-cmake/blob/main/xrepo.cmake) 提供`xrepo_package`函数来管理包。 ```cmake xrepo_package( "foo 1.2.3" [CONFIGS feature1=true,feature2=false] [CONFIGS path/to/script.lua] [DEPS] [MODE debug|release] [ALIAS aliasname] [OUTPUT verbose|diagnosis|quiet] [DIRECTORY_SCOPE] ) ``` 部分函数参数直接对应 Xrepo 命令选项。 `xrepo_package` 会将软件包安装目录添加到 `CMAKE_PREFIX_PATH`,因此 `find_package` 可以直接使用。如果 `CMAKE_MINIMUM_REQUIRED_VERSION` >= 3.1,cmake 的 `pkgConfig` 也会搜索软件包安装目录下的 pkgconfig 文件。 调用 `xrepo_package(foo)` 后,`foo` 包有三种使用方式: 1. 如果包提供 cmake 配置文件,则调用 `find_package(foo)`。 * 有关详细信息,请参阅 CMake [`find_package`](https://cmake.org/cmake/help/latest/command/find_package.html) 文档。 2. 如果包没有提供 cmake 配置文件或者找不到模块 * 以下变量可用于使用pacakge(cmake后的变量名 查找模块 [标准变量名称](https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#standard-variable-names)) * `foo_INCLUDE_DIRS` * `foo_LIBRARY_DIRS` * `foo_LIBRARIES` * `foo_DEFINITIONS` * 如果指定了 `DIRECTORY_SCOPE`,则 `xrepo_package` 会运行以下代码 ```cmake include_directories(${foo_INCLUDE_DIRS}) link_directories(${foo_LIBRARY_DIRS}) ``` 3. 也可以使用 `xrepo_target_packages`,详见下文。 注意:`CONFIGS path/to/script.lua` 用于对包配置进行精细控制。 例如: * 排除系统上的包。 * 覆盖依赖包的默认配置,例如设置`shared=true`。 如果指定了 `DEPS`,所有依赖库都将添加到 `CMAKE_PREFIX_PATH`,以及 include 和 libraries 那四个变量中。 ### xrepo\_target\_packages 将包的 includedirs 和 links/linkdirs 添加到指定目标。 ```cmake xrepo_target_packages( target [NO_LINK_LIBRARIES] [PRIVATE|PUBLIC|INTERFACE] package1 package2 ... ) ``` ## 使用来自官方存储库的包 Xrepo 官方仓库:[xmake-repo](https://github.com/xmake-io/xmake-repo) 下面是一个使用 `gflags` 包版本 2.2.2 的 `CMakeLists.txt` 示例,由 Xrepo 管理。 ### 集成 xrepo.cmake ```cmake cmake_minimum_required(VERSION 3.13.0) project(foo) # 如果 build 目录下不存在 xrepo.cmake,则下载。 if(NOT EXISTS "${CMAKE_BINARY_DIR}/xrepo.cmake") message(STATUS "Downloading xrepo.cmake from https://github.com/xmake-io/xrepo-cmake/") # mirror https://cdn.jsdelivr.net/gh/xmake-io/xrepo-cmake@main/xrepo.cmake file(DOWNLOAD "https://raw.githubusercontent.com/xmake-io/xrepo-cmake/main/xrepo.cmake" "${CMAKE_BINARY_DIR}/xrepo.cmake" TLS_VERIFY ON) endif() # 包含 xrepo.cmake 以便可以使用 xrepo_package 函数。 include(${CMAKE_BINARY_DIR}/xrepo.cmake) ``` ### 添加包 ```cmake xrepo_package("zlib") add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) xrepo_target_packages(example-bin zlib) ``` ### 添加带有配置的包 ```cmake xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true") add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) xrepo_target_packages(example-bin gflags) ``` ### 添加带有 cmake 导入模块的包 ```cmake xrepo_package("gflags 2.2.2" CONFIGS "shared=true,mt=true") # `xrepo_package` 会将 gflags 安装目录添加到 CMAKE_PREFIX_PATH。 # `find_package(gflags)` 会从 CMAKE_PREFIX_PATH 包含的目录中找到 gflags 提供的 config-file 文件。 # 参考 https://cmake.org/cmake/help/latest/command/find_package.html#search-modes find_package(gflags CONFIG COMPONENTS shared) add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) target_link_libraries(example-bin gflags) ``` ### 添加自定义包 ```cmake set(XREPO_XMAKEFILE ${CMAKE_CURRENT_SOURCE_DIR}/packages/xmake.lua) xrepo_package("myzlib") add_executable(example-bin "") target_sources(example-bin PRIVATE src/main.cpp ) xrepo_target_packages(example-bin myzlib) ``` 在 packages/xmake.lua 中定义一个包: ```lua package("myzlib") -- ... ``` 我们可以自定义一个包,具体定义方式见文档:[自定义 Xrepo 包](/zh/guide/package-management/package-distribution#define-package-configuration)。 ## 使用来自第三个存储库的包 除了从官方维护的存储库安装软件包外,Xrepo 还可以安装来自第三方包管理器的包,例如 vcpkg、conan、conda、pacman、homebrew、apt、dub、cargo。 关于命令行的使用,我们可以参考文档:[Xrepo命令用法](/zh/guide/package-management/xrepo-cli). 我们也可以直接在 cmake 中使用它来安装来自第三方仓库的包,只需将仓库名称添加为命名空间即可。例如:`vcpkg::zlib`、`conan::pcre2` ### Conan ```cmake xrepo_package("conan::gflags/2.2.2") ``` ### Conda ```cmake xrepo_package("conda::gflags 2.2.2") ``` ### Vcpkg ```cmake xrepo_package("vcpkg::gflags") ``` ### Homebrew ```cmake xrepo_package("brew::gflags") ``` --- --- url: /zh/guide/project-configuration/multi-level-directories.md --- # 多级配置 {#multi-level-directories} 在脚本域我们可以通过 import 导入各种丰富的扩展模块来使用,而在描述域我们可以通过[includes](/zh/api/description/global-interfaces.html#includes)接口,来引入项目子目录下的xmake.lua配置。 记住:xmake的includes是按照tree结构来处理配置关系的,子目录下的xmake.lua里面的target配置会继承父xmake.lua中的根域配置,例如: 目前有如下项目结构: ``` projectdir - xmake.lua - src - xmake.lua ``` `projectdir/xmake.lua`是项目的根xmake.lua配置,而`src/xmake.lua`是项目的子配置。 `projectdir/xmake.lua`内容: ```lua add_defines("ROOT") target("test1") set_kind("binary") add_files("src/*.c") add_defines("TEST1") target("test2") set_kind("binary") add_files("src/*.c") add_defines("TEST2") includes("src") ``` 里面全局根域配置了`add_defines("ROOT")`,会影响下面的所有target配置,包括includes里面子xmake.lua中的所有target配置,所以这个是全局总配置。 而在test1/test2里面的`add_defines("TEST1")`和`add_defines("TEST2")`属于局部配置,只对当前target生效。 `src/xmake.lua`内容: ```lua add_defines("ROOT2") target("test3") set_kind("binary") add_files("src/*.c") add_defines("TEST3") ``` 在`src/xmake.lua`子配置中,也有个全局根域,配置了`add_defines("ROOT2")`,这个属于子配置根域,只对当前子xmake.lua里面所有target生效,也会对下级includes里面的子xmake.lua中target生效,因为之前说了,xmake是tree状结构的配置继承关系。 所以,这几个target的最终配置结果依次是: ``` target("test1"): -DROOT -DTEST1 target("test2"): -DROOT -DTEST2 target("test3"): -DROOT -DROOT2 -DTEST3 ``` --- --- url: /zh/posts/includes-check.md --- 为了进一步提升构建效率,减少没必要的重建,xmake新增了对头文件的依赖检测,以及自动构建仅仅需要重新编译的源文件,提升编译速度,并且完全支持windows、linux、macosx等大部分平台。。 由于检测过程本身也会有一些性能损耗,因此xmake对此进行了深度优化,实现极速依赖检测: * 对依赖头文件进行过滤,如果是系统头文件,非自身项目的第三方头文件,自动忽略,这些头文件基本上不会再开发项目的时候,经常变动,所以没必要去每次检测他们,如果真有变动,手动重建下就行了 * 针对每个头文件的检测结果进行缓存,直接应用到下一个源文件上,减少重复检测的次数 * 其他一些细节优化 验证效果: 就拿tbox为例,我手动修改了下 tbox 中的正则头文件:`regex.h` 然后编译(注:不是执行重建哦,那个是 `xmake -r`) ```bash $xmake ``` 编译结果: ``` [00%]: ccache compiling.release src/tbox/tbox.c [15%]: ccache compiling.release src/tbox/memory/impl/prefix.c [36%]: ccache compiling.release src/tbox/regex/regex.c [50%]: archiving.release libtbox.a ... ``` 仅仅只编译了其中三个include了用到regex.h的源文件。 当然如果你修改了依赖的第三方库的头文件,最好还是手动重建下: ```bash $xmake -r or $xmake --rebuild ``` --- --- url: /zh/posts/enable-pdb-for-windows.md --- xmake默认情况下是不会去生成pdb文件,就算是debug编译,启用了调试符号: ```lua set_symbols("debug") ``` 也是不会生成额外的pdb文件,它会把所有调试符号内置到程序里面,如果要独立生成pdb文件,可以对`xmake.lua`进行如下修改: ```lua -- 先禁用内置的调试符号开关 --set_symbols("debug") -- 静态库目标 target("test") set_kind("static") -- 仅针对windows平台 if is_plat("windows") then -- 启用pdb生成 add_cxflags("-ZI", "-Fd$(buildir)\\test.pdb") add_ldflags("-pdb:$(buildir)\\test.pdb") add_arflags("-pdb:$(buildir)\\test.pdb") end -- 可执行目标 target("demo") set_kind("binary") add_deps("test") add_links("test") -- 仅针对windows平台 if is_plat("windows") then -- 启用pdb生成 add_cxflags("-ZI", "-Fd$(buildir)\\demo.pdb") add_ldflags("-pdb:$(buildir)\\demo.pdb") end ``` --- --- url: /zh/posts/how-to-build-a-simple-project.md --- 首先我们通过内置的工程模板创建一个空工程: ```bash $ xmake create -P ./hello create hello ... create ok!👌 ``` 这个时候xmake将会产生一些工程文件,如下: ```bash $ cd ./hello $ tree . . ├── src │   └── main.c └── xmake.lua ``` 这个简单的程序仅仅只是为了打印输出: `hello xmake!` ```bash $ cat ./src/main.c #include int main(int argc, char** argv) { printf("hello xmake!\n"); return 0; } ``` `xmake.lua`是基于lua语法的工程描述文件,它很简单: ```lua $ cat xmake.lua target("hello") set_kind("binary") add_files("src/*.c") ``` 现在我们开始编译这个程序 ```bash $ xmake checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.11 checking for the target minimal version ... 10.11 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the debugger (dd) ... xcrun -sdk macosx lldb configure { ex = "xcrun -sdk macosx ar" , ccache = "ccache" , plat = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , sh = "xcrun -sdk macosx clang++" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , xcode_dir = "/Applications/Xcode.app" , target_minver = "10.11" , sc = "xcrun -sdk macosx swiftc" , mode = "release" , make = "make" , cc = "xcrun -sdk macosx clang" , host = "macosx" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.11" , cxx = "xcrun -sdk macosx clang" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: ccache compiling.release src/main.c [100%]: linking.release hello build ok!👌 ``` 接着运行它: ```bash $ xmake run hello hello world! ``` 或者进行调试 ```bash $ xmake run -d hello [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]$ ``` 接着我们尝试构建一个android版本,这个时候得设置ndk路径,当然也能配置到全局配置中,一劳永逸 ```bash $ xmake f -p android --ndk=~/files/android-ndk-r10e/ checking for the architecture ... armv7-a checking for the SDK version of NDK ... android-21 checking for the c compiler (cc) ... arm-linux-androideabi-gcc checking for the c++ compiler (cxx) ... arm-linux-androideabi-g++ checking for the assember (as) ... arm-linux-androideabi-gcc checking for the linker (ld) ... arm-linux-androideabi-g++ checking for the static library archiver (ar) ... arm-linux-androideabi-ar checking for the static library extractor (ex) ... arm-linux-androideabi-ar checking for the shared library linker (sh) ... arm-linux-androideabi-g++ configure { ex = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar" , ccache = "ccache" , ndk = "~/files/android-ndk-r10e/" , sc = "xcrun -sdk macosx swiftc" , ar = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar" , ld = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++" , buildir = "build" , host = "macosx" , as = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc" , toolchains = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin" , arch = "armv7-a" , mxx = "xcrun -sdk macosx clang++" , xcode_dir = "/Applications/Xcode.app" , target_minver = "10.11" , ndk_sdkver = 21 , mode = "release" , cc = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc" , cxx = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++" , make = "make" , dd = "xcrun -sdk macosx lldb" , kind = "static" , sh = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++" , xcode_sdkver = "10.11" , plat = "android" , mm = "xcrun -sdk macosx clang" } configure ok! $ xmake clean ok! [00%]: ccache compiling.release src/main.c [100%]: linking.release hello build ok!👌 ``` 或者我们编一个iphoneos的版本,例如: ```bash $ xmake f -p iphoneos checking for the architecture ... armv7 checking for the Xcode SDK version for iphoneos ... 9.2 checking for the target minimal version ... 9.2 checking for the c compiler (cc) ... xcrun -sdk iphoneos clang checking for the c++ compiler (cxx) ... xcrun -sdk iphoneos clang checking for the objc compiler (mm) ... xcrun -sdk iphoneos clang checking for the objc++ compiler (mxx) ... xcrun -sdk iphoneos clang++ checking for the assember (as) ... gas-preprocessor.pl xcrun -sdk iphoneos clang checking for the linker (ld) ... xcrun -sdk iphoneos clang++ checking for the static library archiver (ar) ... xcrun -sdk iphoneos ar checking for the static library extractor (ex) ... xcrun -sdk iphoneos ar checking for the shared library linker (sh) ... xcrun -sdk iphoneos clang++ checking for the swift compiler (sc) ... xcrun -sdk iphoneos swiftc configure { ex = "xcrun -sdk iphoneos ar" , ccache = "ccache" , ndk = "~/files/android-ndk-r10e/" , sc = "xcrun -sdk iphoneos swiftc" , ar = "xcrun -sdk iphoneos ar" , sh = "xcrun -sdk iphoneos clang++" , buildir = "build" , xcode_dir = "/Applications/Xcode.app" , as = "/usr/local/share/xmake/tools/utils/gas-preprocessor.pl xcrun -sdk iphoneos clang" , toolchains = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin" , arch = "armv7" , mxx = "xcrun -sdk iphoneos clang++" , ndk_sdkver = 21 , target_minver = "9.2" , cc = "xcrun -sdk iphoneos clang" , mode = "release" , host = "macosx" , cxx = "xcrun -sdk iphoneos clang" , make = "make" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk iphoneos clang++" , xcode_sdkver = "9.2" , plat = "iphoneos" , mm = "xcrun -sdk iphoneos clang" } configure ok! $ xmake [00%]: ccache compiling.release src/main.c [100%]: linking.release hello build ok!👌 ``` 最后我们尝试为mingw平台进行编译,sdk指定交叉工具链目录,交叉编译linux平台也可以这么用哦。。 ```bash $ xmake f -p mingw --sdk=/usr/local/i386-mingw32-4.3.0/ checking for the architecture ... i386 checking for the c compiler (cc) ... i386-mingw32-gcc checking for the c++ compiler (cxx) ... i386-mingw32-g++ checking for the assember (as) ... i386-mingw32-gcc checking for the linker (ld) ... i386-mingw32-g++ checking for the static library archiver (ar) ... i386-mingw32-ar checking for the static library extractor (ex) ... i386-mingw32-ar checking for the shared library linker (sh) ... i386-mingw32-g++ checking for the swift compiler (sc) ... no configure { ex = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-ar" , ccache = "ccache" , ndk = "~/files/android-ndk-r10e/" , sc = "xcrun -sdk iphoneos swiftc" , sdk = "/usr/local/i386-mingw32-4.3.0/" , cc = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-gcc" , ndk_sdkver = 21 , buildir = "build" , plat = "mingw" , as = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-gcc" , toolchains = "/Users/ruki/files/android-ndk-r10e/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin" , arch = "i386" , mxx = "xcrun -sdk iphoneos clang++" , xcode_dir = "/Applications/Xcode.app" , target_minver = "9.2" , sh = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-g++" , mode = "release" , host = "macosx" , cxx = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-g++" , make = "make" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ar = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-ar" , xcode_sdkver = "9.2" , ld = "/usr/local/i386-mingw32-4.3.0/bin/i386-mingw32-g++" , mm = "xcrun -sdk iphoneos clang" } configure ok! $ xmake [00%]: ccache compiling.release src/main.c [100%]: linking.release hello.exe build ok!👌 ``` xmake还能直接在windows的cmd终端下,进行直接编译windows的程序,它会去自动检测当前系统装的vs环境,调用里面的cl.exe编译器进行编译,一切都是自动化的,我们不需要额外配置什么,只需要执行:`xmake` 就行了。。 例如: ```bash $ xmake checking for the architecture ... x86 checking for the Microsoft Visual Studio version ... 2008 checking for the c compiler (cc) ... cl.exe checking for the c++ compiler (cxx) ... cl.exe checking for the assember (as) ... ml.exe checking for the linker (ld) ... link.exe checking for the static library archiver (ar) ... link.exe -lib checking for the shared library linker (sh) ... link.exe -dll checking for the static library extractor (ex) ... lib.exe configure { ex = "lib.exe" , sh = "link.exe -dll" , host = "windows" , ar = "link.exe -lib" , as = "ml.exe" , plat = "windows" , buildir = "build" , arch = "x86" , cc = "cl.exe" , cxx = "cl.exe" , mode = "release" , clean = true , kind = "static" , ld = "link.exe" , vs = "2008" } configure ok! [00%]: compiling.release src\main.c [100%]: linking.release hello.exe build ok! ``` 顺便说一下,在windows下编译,xmake是完全支持多任务的哦,默认就是自动多任务构建的,比起以前在msys, cygwin里面用 gmake来编译快多了,因为windows下的gmake就算你启用了`-j 4` 也没啥效果,非常非常得慢。。。 --- --- url: /zh/posts/how-to-compile-on-cross-toolchains.md --- xmake 提供了方便灵活的交叉编译支持,大部分情况下,都不需要配置很复杂的toolchains前缀,例如:`arm-linux-` 什么的 只要这个toolchains目录满足如下结构(大部分的交叉工具链都是这个结构): ``` /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目录,并不在 `/home/toolchains_sdkdir/bin` 这个位置,而是独立到了 `/usr/opt/bin` , 那怎么办呢,其实也不麻烦,配置的时候,再指定下bin目录的位置就好: ```bash $ xmake f -p linux --sdk=/home/toolchains_sdkdir --toolchains=/usr/opt/bin $ xmake ``` 如果这个工具链非常奇葩,就是不按规则出牌,路径规则很乱的话,那么xmake也没办法那么智能,只能手动配置全所有参数了: ```bash $ xmake f -p linux --sdk=/home/toolchains_sdkdir --toolchains=/usr/opt/bin --cxflags="-I/usr/xxx/include" --ldflags="-L/usr/zzz/lib" $ xmake ``` 另外,如果交叉工具链的前缀,例如:`arm-linux-` xmake 没有检测成功,你也可以通过`--cross=`参数手动配置上它: ```bash $ xmake f -p linux --cross=arm-linux- --sdk=/home/toolchains_sdkdir ... ``` --- --- url: /zh/guide/basic-commands/install-and-uninstall.md --- # 安装卸载 {#install-and-uninstall} 当编译完成,我们也可以通过 `xmake install` 命令,将构建目标程序和库安装到系统环境。 这通常用于 linux, unix, bsd 等系统环境下的安装。 ::: tip 注意 尽管 Windows 下也支持,但是不常用,因为 Windows 上没有默认的安装目录,因此更推荐直接生成安装包。具体见:[XPack 打包](/zh/api/description/xpack-interfaces) ::: ## 命令格式 ::: code-group ```sh [安装] $ xmake install [options] [target] ``` ```sh [卸载] $ xmake uninstall [options] [target] ``` ::: ## 安装到系统目录 通常我们只需要执行下面的命令,即可完成安装,默认的安装目录在 `/usr/local` 下。 ```sh $ xmake install ``` ## 安装到指定目录 我们也可以指定需要安装的根目录,尤其是对于 Windows 上没有默认安装目录的情况下,会更有用些。 ```sh $ xmake install -o /tmp/usr installing foo .. installing foo to /tmp/usr .. installing test .. installing test to /tmp/usr .. install ok! ruki:test ruki$ tree /tmp/usr /tmp/usr ├── bin │   └── test └── lib └── libfoo.a 2 directories, 2 files ``` 另外,我们也可以通过 [set\_installdir](/zh/api/description/project-target#set-installdir) 接口,在配置文件中进行默认安装路径的配置,具体可以看 API 手册里面的详细说明。 ## 卸载程序 我们也可以通过 `xmake uninstall` 执行反向的卸载操作,同样也可以指定需要卸载的安装目录。 ```sh $ xmake uninstall $ xmake uninstall --installdir=/tmp/usr ``` --- --- url: /zh/guide/project-configuration/define-options.md --- # 定义选项 {#define-option} ## 自定义命令行选项 我们可以定义一个选项开关,用于控制内部的配置逻辑,例如: ```lua option("tests", {default = false, description = "Enable Tests"}) target("foo") set_kind("binary") add_files("src/*.cpp") if has_config("tests") then add_defines("TESTS") end ``` 然后,我们可以命令行启用这个自定义的选项,使得 foo 目标编译时候自动加上`-DTEST`的宏定义。 ```sh $ xmake f --tests=y $ xmake ``` 上面的 option 配置由于比较简单,所以我们使用了单行简化写法,我们也可以使用完整的域配置写法。 ```lua option("tests") set_default(false) set_description("Enable Tests") ``` ## 绑定选项到目标 我们也可以不使用 `has_config` 和 `add_defines` 去手动设置,直接使用 `add_options` 将选项绑定到指定的 target。 这样,当选项被启用的时候,tests 选项中所有关联的设置都会自动设置到被绑定的目标中。 ```lua option("tests") set_description("Enable Tests") set_default(false) add_defines("TEST") target("foo") set_kind("binary") add_files("src/*.cpp") add_options("tests") ``` 上面的例子,当 tests 启用后,foo 目标会自动添加上 `-DTEST`。 ## 选项类型与常用接口 {#option-types-apis} ### 选项类型 * **布尔型**:开关选项,常用于启用/禁用某特性 * **字符串型**:用于路径、模式等自定义值 * **多值型**:通过 `set_values` 提供可选项(配合菜单) ### 常用接口 * `set_default(value)`:设置默认值(支持 bool 或 string) * `set_showmenu(true/false)`:是否在 `xmake f --menu` 菜单中显示 * `set_description("desc")`:设置描述 * `set_values("a", "b", "c")`:设置可选值(菜单模式) * `add_defines("FOO")`:启用时自动添加宏定义 * `add_links("bar")`:启用时自动链接库 * `add_cflags("-O2")`:启用时自动添加编译选项 ## 选项依赖与条件控制 {#option-deps} * `add_deps("otheropt")`:依赖其他选项,常用于 on\_check/after\_check 控制 * `before_check`/`on_check`/`after_check`:自定义检测逻辑,可动态启用/禁用选项 #### 依赖示例 ```lua option("feature_x") set_default(false) on_check(function (option) if has_config("feature_y") then option:enable(false) end end) ``` ## 选项实例接口 {#option-instance} 在 `on_check`、`after_check` 等脚本中,可以通过 option 实例接口获取和设置选项状态: * `option:name()` 获取选项名 * `option:value()` 获取选项值 * `option:enable(true/false)` 启用/禁用选项 * `option:enabled()` 判断选项是否启用 * `option:get("defines")` 获取配置值 * `option:set("defines", "FOO")` 设置配置值 * `option:add("links", "bar")` 追加配置值 ## 选项与 target 的结合 {#option-in-target} * 通过 `add_options("opt1", "opt2")` 绑定选项到目标 * 选项启用时,相关配置会自动应用到目标 * 也可用 `has_config("opt")` 在 target 域内做条件判断 ## 典型示例 {#option-examples} ### 1. 布尔开关选项 ```lua option("enable_lto") set_default(false) set_showmenu(true) set_description("Enable LTO optimization") add_cflags("-flto") target("foo") add_options("enable_lto") ``` ### 2. 路径/字符串选项 ```lua option("rootdir") set_default("/tmp/") set_showmenu(true) set_description("Set root directory") target("foo") add_files("$(rootdir)/*.c") ``` ### 3. 多值菜单选项 ```lua option("arch") set_default("x86_64") set_showmenu(true) set_description("Select architecture") set_values("x86_64", "arm64", "mips") ``` ## 更多信息 {#more-information} * 完整 API 文档:[选项 API](/zh/api/description/configuration-option) * 选项实例接口:[option 实例 API](/zh/api/scripts/option-instance) --- --- url: /zh/guide/extras/trybuild-3rd-sourcecode.md --- # 尝试构建第三方源码 {#trybuild-3rd-sourcecode} Xmake v2.3.1 以上版本直接对接了其他第三方构建系统,即使其他项目中没有使用 xmake.lua 来维护,Xmake 也可以直接调用其他构建工具来完成编译。 那用户直接调用使用第三方构建工具来编译不就行了,为啥还要用 xmake 去调用呢?主要有以下好处: 1. 完全的行为一致,简化编译流程,不管用了哪个其他构建系统,都只需要执行 xmake 这个命令就可以编译,用户不再需要去研究其他工具的不同编译流程。 2. 完全对接 `xmake config` 的配置环境,复用 xmake 的平台探测和 sdk 环境检测,简化平台配置。 3. 对接交叉编译环境,即使是用 autotools 维护的项目,也能通过 xmake 快速实现交叉编译。 目前已支持的构建系统: * autotools(已完全对接xmake的交叉编译环境) * xcodebuild * cmake(已完全对接xmake的交叉编译环境) * make * msbuild * scons * meson * bazel * ndkbuild * ninja ## 自动探测构建系统并编译 例如,对于一个使用 cmake 维护的项目,直接在项目根目录执行 xmake,就会自动触发探测机制,检测到 CMakeLists.txt,然后提示用户是否需要使用 cmake 来继续完成编译。 ```sh $ xmake note: CMakeLists.txt found, try building it (pass -y or --confirm=y/n/d to skip confirm)? please input: y (y/n) -- Symbol prefix: -- Configuring done -- Generating done -- Build files have been written to: /Users/ruki/Downloads/libpng-1.6.35/build [ 7%] Built target png-fix-itxt [ 21%] Built target genfiles [ 81%] Built target png [ 83%] Built target png_static ... output to /Users/ruki/Downloads/libpng-1.6.35/build/artifacts build ok! ``` ## 无缝对接xmake命令 目前支持 `xmake clean`、`xmake --rebuild` 和 `xmake config` 等常用命令与第三方系统的无缝对接。 我们可以直接清理 cmake 维护项目的编译输出文件 ```sh $ xmake clean $ xmake clean --all ``` 如果带上 `--all` 执行清理,会清除 autotools/cmake 生成的所有文件,不仅仅只清理对象文件。 默认 `xmake` 对接的是增量构建行为,不过我们也可以强制快速重建: ```sh $ xmake --rebuild ``` ## 手动切换指定构建系统 如果一个项目下有多个构建系统同时在维护,比如 libpng 项目,自带 autotools/cmake/makefile 等构建系统维护,xmake 默认优先探测使用 autotools,如果想要强制切换其他构建系统,可以执行: ```sh $ xmake f --trybuild=[autotools|cmake|make|msbuild| ..] $ xmake ``` 另外,配置了 `--trybuild=` 参数手动指定了默认的构建系统,后续的 build 过程就不会额外提示用户选择了。 ## 实现快速交叉编译 众所周知,cmake/autotools 维护的项目虽然很多都支持交叉编译,但是交叉编译的配置过程很复杂,不同的工具链处理方式还有很多差异,中途会踩到很多坑。 即使跑通了一个工具链的交叉编译,如果切到另外一个工具链环境,可能又要折腾好久,而如果使用 xmake,通常只需要两条简单的命令即可: ::: tip 注意 目前 cmake/autotools 都已对接支持了 xmake 的交叉编译。 ::: ### 交叉编译 android 平台 ```sh $ xmake f -p android --trybuild=autotools [--ndk=xxx] $ xmake ``` ::: tip 注意 其中,--ndk 参数配置是可选的,如果用户设置了 ANDROID\_NDK\_HOME 环境变量,或者 ndk 放置在 ~/Library/Android/sdk/ndk-bundle,xmake 都能自动检测到。 ::: 是不是很简单?如果你觉得这没啥,那么可以对比下直接操作 `./configure` 去配置交叉编译,可以看下这篇文档对比下:[将 NDK 与其他编译系统配合使用](https://developer.android.com/ndk/guides/other_build_systems#autoconf) 说白了,你大概得这样,还不一定一次就能搞定: ```sh $ export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/$HOST_TAG $ export AR=$TOOLCHAIN/bin/aarch64-linux-android-ar $ export AS=$TOOLCHAIN/bin/aarch64-linux-android-as $ export CC=$TOOLCHAIN/bin/aarch64-linux-android21-clang $ export CXX=$TOOLCHAIN/bin/aarch64-linux-android21-clang++ $ export LD=$TOOLCHAIN/bin/aarch64-linux-android-ld $ export RANLIB=$TOOLCHAIN/bin/aarch64-linux-android-ranlib $ export STRIP=$TOOLCHAIN/bin/aarch64-linux-android-strip $ ./configure --host aarch64-linux-android $ make ``` 如果是 cmake 呢,交叉编译也不省事,对于 android 平台,得这么配置。 ```sh $ cmake \ -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ -DANDROID_ABI=$ABI \ -DANDROID_NATIVE_API_LEVEL=$MINSDKVERSION \ $OTHER_ARGS ``` 而对于 ios 平台,没找到简单的配置方式,就找了个第三方的 ios 工具链配置,很复杂:https://github.com/leetal/ios-cmake/blob/master/ios.toolchain.cmake 对于 mingw 又是另外一种方式,我又折腾了半天环境,很是折腾。 而对接了 xmake 后,不管是 cmake 还是 autotools,交叉编译都是非常简单的,而且配置方式也完全一样,精简一致。 ### 交叉编译 iphoneos 平台 ```sh $ xmake f -p iphoneos --trybuild=[cmake|autotools] $ xmake ``` ### 交叉编译 mingw 平台 ```sh $ xmake f -p mingw --trybuild=[cmake|autotools] [--mingw=xxx] $ xmake ``` ### 使用其他交叉编译工具链 ```sh $ xmake f -p cross --trybuild=[cmake|autotools] --sdk=/xxxx $ xmake ``` 关于更多交叉编译的配置细节,请参考文档:[交叉编译](/zh/guide/basic-commands/cross-compilation),除了多了一个 `--trybuild=` 参数,其他交叉编译配置参数都是完全通用的。 ## 传递用户配置参数 我们可以通过 `--tryconfigs=` 来传递用户额外的配置参数到对应的第三方构建系统,比如:autotools 会传递给 `./configure`,cmake 会传递给 `cmake` 命令。 ```sh $ xmake f --trybuild=autotools --tryconfigs="--enable-shared=no" $ xmake ``` 比如上述命令,传递 `--enable-shared=no` 给 `./configure`,来禁用动态库编译。 另外,对于 `--cflags`, `--includedirs` 和 `--ldflags` 等参数,不需要通过 `--tryconfigs`,通过 `xmake config --cflags=` 等内置参数就可透传过去。 ## 编译其他构建系统过程示例 ### 通用编译方式 大多数情况下,每个构建系统对接后的编译方式都是一致的,除了 `--trybuild=` 配置参数除外。 ```sh $ xmake f --trybuild=[autotools|cmake|meson|ninja|bazel|make|msbuild|xcodebuild] $ xmake ``` ::: tip 注意 我们还需要确保 --trybuild 指定的构建工具已经安装能够正常使用。 ::: ### 构建 Android jni 程序 如果当前项目下存在 `jni/Android.mk`,那么 xmake 可以直接调用 ndk-build 来构建 jni 库。 ```sh $ xmake f -p android --trybuild=ndkbuild [--ndk=] $ xmake ``` 如果觉得命令行编译 jni 比较麻烦,xmake 也提供了相关的 gradle 集成插件 [xmake-gradle](https://github.com/xmake-io/xmake-gradle), 可以无缝集成 xmake 进行 jni 库的编译集成,具体详情见:[使用 xmake-gradle 插件构建 JNI 程序](/zh/guide/extensions/ide-integration-plugins.html#gradle-plugin)。 --- --- url: /zh/guide/project-configuration/toolchain-configuration.md --- # 工具链配置 {#toolchain-configuration} ## 切换工具链 {#switch-toolchains} 之前,我们讲到到,可以通过命令行 `xmake f --toolchain=[name]` 来全局切换工具链。想要了解详情,见:[命令行工具链切换](/zh/guide/basic-commands/switch-toolchains)。 在命令行中切换,尽管快速方便,但是它只能全局切换,如果工程中有多个 target 目标,我们仅仅想要对其中某个 target 进行工具链切换,那么我们只能在配置文件中使用 [set\_toolchains](/zh/api/description/project-target.html#set-toolchains) 进行配置。 例如: ```lua target("test")     set_kind("binary")     add_files("src/*.c")     set_toolchains("clang", "yasm") ``` 当然,我们如果我们将 set\_toolchains 放置在配置文件的顶层根作用域,那么它会对所有 targets 目标生效,也能起到全局配置切换的效果。 ```lua set_toolchains("clang", "yasm") target("foo")     set_kind("binary")     add_files("src/*.c") target("bar")     set_kind("binary")     add_files("src/*.c") ``` 关于这个接口的更多描述,可以到 [set\_toolchains](/zh/api/description/project-target#set-toolchains) API 手册页详细了解。 ## 切换工具集 {#switch-toolsets} 除了通过 set\_toolchains 进行工具链切换,我们还可以使用 set\_toolset 接口对 target 局部切换某个编译器。 它跟 set\_toolchains 区别在于,toolchain 是包含 编译器,链接器,库归档器和汇编器等所有系列工具的集合,而 toolset 仅仅只会单独切换某个工具链内部的某个编译工具。 ```lua target("test1") add_files("*.c") target("test2") add_files("*.c") set_toolset("cc", "$(projectdir)/tools/bin/clang-5.0") ``` :::tip 注意 它的粒度更加的小,但是除非必要,我们还是更建议用户使用 set\_toolchains 实现统一的切换。 ::: 关于这个接口的更多描述,可以到 [set\_toolset](/zh/api/description/project-target#set-set_toolset) API 手册页详细了解。 ## 自定义工具链 {#custom-toolchains} 我们还可以通过 toolchain 接口,实现自定义工具链,例如: ```lua toolchain("my_muslcc") set_homepage("https://musl.cc/") set_description("The musl-based cross-compilation toolchains") set_kind("cross") on_load(function (toolchain) toolchain:load_cross_toolchain() if toolchain:is_arch("arm") then toolchain:add("cxflags", "-march=armv7-a", "-msoft-float", {force = true}) toolchain:add("ldflags", "-march=armv7-a", "-msoft-float", {force = true}) end toolchain:add("syslinks", "gcc", "c") end) toolchain_end() target("test") set_kind("binary") add_files("src/*.c") set_toolchains("my_muslcc") ``` 这里,我们自定义了一个 my\_muslcc 交叉编译工具链,并且将它设置到了 test 目标中。 关于自定义工具链,可以到 [自定义工具链](/zh/api/description/custom-toolchain) 手册也进一步了解。 ## 远程自动拉取工具链 {#pull-remote-toolchains} 从 2.5.2 版本开始,我们可以拉取指定的工具链来集成编译项目,我们也支持将依赖包切换到对应的远程工具链参与编译后集成进来。 相关例子代码见:[Toolchain/Packages Examples](https://github.com/xmake-io/xmake/tree/master/tests/projects/package) 相关的 issue [#1217](https://github.com/xmake-io/xmake/issues/1217) ### 拉取指定版本的 llvm 工具链 我们使用 llvm-10 中的 clang 来编译项目。 ```lua add_requires("llvm 10.x", {alias = "llvm-10"}) target("test") set_kind("binary") add_files("src/*.c) set_toolchains("llvm@llvm-10") ``` ### 拉取交叉编译工具链 {#pull-and-bind-cross-toolchain} 我们也可以拉取指定的交叉编译工具链来编译项目。 ```lua add_requires("muslcc") target("test") set_kind("binary") add_files("src/*.c) set_toolchains("@muslcc") ``` ### 拉取工具链并且集成对应工具链编译的依赖包 我们也可以使用指定的muslcc交叉编译工具链去编译和集成所有的依赖包。 ```lua add_requires("muslcc") add_requires("zlib", "libogg", {system = false}) set_toolchains("@muslcc") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib", "libogg") ``` 完整例子见:[Examples (muslcc)](https://github.com/xmake-io/xmake/blob/master/tests/projects/package/toolchain_muslcc/xmake.lua) ### 拉取集成 Zig 工具链 ```lua add_rules("mode.debug", "mode.release") add_requires("zig 0.7.x") target("test") set_kind("binary") add_files("src/*.zig") set_toolchains("@zig") ``` ### 拉取绑定自定义工具链 我们也可以自定义一个工具链 `my_muslcc`,并且绑定到对应的工具链包 `muslcc` 中。 ```lua toolchain("my_muslcc") set_homepage("https://musl.cc/") set_description("The musl-based cross-compilation toolchains") set_kind("cross") on_load(function (toolchain) toolchain:load_cross_toolchain() if toolchain:is_arch("arm") then toolchain:add("cxflags", "-march=armv7-a", "-msoft-float", {force = true}) toolchain:add("ldflags", "-march=armv7-a", "-msoft-float", {force = true}) end toolchain:add("syslinks", "gcc", "c") end) toolchain_end() add_requires("muslcc") target("test") set_kind("binary") add_files("src/*.c") set_toolchains("my_muslcc@muslcc") ``` :::tip 注意 工具链和包的绑定逻辑,除了配置 `set_toolchains("my_muslcc@muslcc")`,还需要在工具链中自己实现对工具链包的查找才能生效。 ::: 例如在交叉编译工具链的 on\_load 中,需要配置如下这段代码,实现从绑定的工具链包 `muslccc` 安装目录找到对应的工具链。 ```lua for _, package in ipairs(toolchain:packages()) do local installdir = package:installdir() if installdir and os.isdir(installdir) then cross_toolchain = find_cross_toolchain(installdir, {cross = cross}) if cross_toolchain then break end end end ``` 当然,如果我们自定义的是交叉编译工具链,我们也可以简单的调用 `toolchain:load_cross_toolchain()` 实现同样的效果,它内部已经封装了上述的查找逻辑。 ```lua on_load(function (toolchain) toolchain:load_cross_toolchain() end) ``` 更多详情,可以直接参考完整例子:[绑定远程自定义工具链](https://github.com/xmake-io/xmake/blob/dev/tests/projects/package/toolchain_muslcc/xmake.lua) ## 自定义未知编译工具链 之前我们讲的自定义工具链,都是针对一些已知的编译器,例如 gcc, clang, msvc 等,Xmake 内部已经对它们做了适配,知道如何配置编译参数,如何执行编译命令。 如果是完全未知的编译器,它的编译参数和使用跟 gcc, clang 差别非常大的话,Xmake 无法知道如何去调用它们实现编译。 因此,我们需要更进一步的自定义它们,包括对编译参数的映射,编译参数检测,编译命令的生成和执行等等。 我们提供了一个完整的例子,可以直接参考:[完整自定义未知工具链](https://github.com/xmake-io/xmake/tree/dev/tests/apis/custom_toolchain)。 ::: tip 注意 如果编译器的编译参数和使用完全跟 gcc/clang 类似,属于类 gcc/clang 的衍生编译器,仅仅只是工具名称不同,我们并不需要完全自定义它们。 例如,mycompiler.exe 编译器,如果它跟 gcc 的用法类似,那么只需要通过配置 `set_toolset("gcc@mycompiler.exe")`,告诉 xmake,将它强制作为 gcc 编译器来使用即可。 ::: --- --- url: /zh/guide/package-management/package-management-in-project.md --- # 工程内包管理命令 {#package-management-in-project} 包管理命令 `$ xmake require` 可用于手动显式地下载、编译、安装、卸载、检索和查看包信息。 `xmake require` 仅用于当前工程,我们也提供了更为便捷的独立 `xrepo` 包管理器命令,用于全局安装、卸载和查找包。 详细文档见:[Xrepo 命令使用入门](/zh/guide/package-management/xrepo-cli)。 ## 安装指定包 ```sh $ xmake require tbox ``` 安装指定版本包: ```sh $ xmake require tbox "~1.6" ``` 强制重新下载安装,并显示详细安装信息: ```sh $ xmake require -f -v tbox "1.5.x" ``` 传递额外的设置信息: ```sh $ xmake require --extra="{debug=true,config={small=true}}" tbox ``` 安装 debug 包,并传递 `small=true` 的编译配置信息到包中。 ## 卸载指定包 ```sh $ xmake require --uninstall tbox ``` 这会完全卸载并删除包文件。 ## 查看包详细信息 ```sh $ xmake require --info tbox ``` ## 在当前仓库中搜索包 ```sh $ xmake require --search tbox ``` 该命令支持模糊搜索以及 alua 模式匹配搜索: ```sh $ xmake require --search pcr ``` 会同时搜索到 pcre、pcre2 等包。 ## 列举当前已安装的包 ```sh $ xmake require --list ``` --- --- url: /zh/api/description/project-target.md --- # 工程目标 {#project-target} 定义和设置子工程模块,每个`target`对应一个子工程,最后会生成一个目标程序,有可能是可执行程序,也有可能是库模块。 :::tip 注意 target的接口,都是可以放置在target外面的全局作用域中的,如果在全局中设置,那么会影响所有子工程target。 ::: 例如: ```lua -- 会同时影响test和test2目标 add_defines("DEBUG") target("test") add_files("*.c") target("test2") add_files("*.c") ``` :::tip 注意 `target`域是可以重复进入来实现分离设置的。 ::: ## 可见性设置 (Visibility) {#visibility} 在 xmake 中,许多接口支持 `visibility` 配置参数,用于控制配置的可见性范围。`visibility` 参数接受一个对象,其中包含以下键值对: | 键名 | 描述 | 示例 | |------|------|------| | `public` | 导出给依赖的子目标,当其他目标依赖此目标时,会继承这些配置 | `{public = true}` | | `interface` | 作为接口导出,仅对依赖此目标的其他目标生效,对当前目标本身不生效 | `{interface = true}` | | `private` | 仅对当前目标生效,不会传递给依赖此目标的其他目标 | `{private = true}` | ### 使用示例 ```lua -- 设置公共可见性,会传递给依赖此目标的其他目标 add_defines("PUBLIC_DEFINE", {public = true}) -- 设置接口可见性,仅对依赖此目标的其他目标生效 add_includedirs("include", {interface = true}) -- 设置私有可见性,仅对当前目标生效 add_defines("PRIVATE_DEFINE", {private = true}) ``` ### 默认行为 如果不指定 `visibility` 参数,大多数接口默认使用 `private` 可见性,即配置仅对当前目标生效。 ## target ### 定义工程目标 #### 函数原型 ::: tip API ```lua target(name: , { kind = , files = , ... }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 目标名称字符串,用于标识工程目标 | | kind | 目标类型字符串,可选值:binary、static、shared、object、headeronly、phony | | files | 源文件路径字符串或数组,支持通配符匹配模式 | | ... | 其他配置选项,如 deps、defines、includedirs 等 | #### 用法说明 定义一个新的控制台工程目标,工程名为`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") ``` :::tip 注意 所有根域的设置,会全局影响所有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 ### 结束定义工程目标 #### 函数原型 ::: tip API ```lua 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") ... ``` ## set\_kind ### 设置目标编译类型 #### 函数原型 ::: tip API ```lua set_kind(kind: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | kind | 目标类型字符串,指定编译目标的类型 | #### 用法说明 设置目标类型,目前支持的类型有: | 值 | 描述 | | ------ | -----------| | phony | 假的目标程序 | | binary | 二进制程序 | | static | 静态库程序 | | shared | 动态库程序 | | object | 仅仅编译对象集合 | | headeronly | 仅仅头文件集合 | #### binary * 可执行文件类型 ```lua target("demo") set_kind("binary") add_files("src/*.c") ``` :::tip 注意 2.5.5 开始,如果没有设置 set\_kind 接口,默认就是 binary 类型。 ::: 所以我们简化为: ```lua target("demo") add_files("src/*.c") ``` 甚至: ```lua target("demo", {files = "src/*.c"}) ``` #### static * 静态库目标类型 ```lua target("demo") set_kind("static") add_files("src/*.c") ``` #### shared * 动态库目标类型 ```lua target("demo") set_kind("shared") add_files("src/*.c") ``` #### object * 纯对象文件列表类型 通常用于两个目标程序间,部分对象文件共享,仅仅编译一次。也可以用于分离对象文件列表,配置不同的编译参数。 #### phony * 空目标类型 它是一个特殊的目标程序类型,它不生成任何实际的程序文件,仅仅用于组合其他目标程序的依赖关系。 ```lua target("test1") set_kind("binary") add_files("src/*.c") target("test2") set_kind("binary") add_files("src/*.c") target("demo") set_kind("phony") add_deps("test1", "test2") ``` 比如上述配置,我们就可以在执行 `xmake build demo` 编译的时候,同时编译相关的两个依赖程序:test1和test2。 #### headeronly * 纯头文件目标类型 2.5.9 之后,我们新增了 `headeronly` 目标类型,这个类型的目标程序,我们不会实际编译它们,因为它没有源文件需要被编译。 但是它包含了头文件列表,这通常用于 headeronly 库项目的安装,IDE 工程的文件列表生成,以及安装阶段的 cmake/pkgconfig 导入文件的生成。 例如: ```lua add_rules("mode.release", "mode.debug") target("foo") set_kind("headeronly") add_headerfiles("src/foo.h") add_rules("utils.install.cmake_importfiles") add_rules("utils.install.pkgconfig_importfiles") ``` 更多详情见:[#1747](https://github.com/xmake-io/xmake/issues/1747) ## set\_strip ### 设置是否strip信息 #### 函数原型 ::: tip API ```lua set_strip(strip: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | strip | strip模式字符串,可选值:debug、all | #### 用法说明 设置当前目标的strip模式,目前支持一下模式: | 值 | 描述 | | ------ | ----------------------------------------- | | debug | 链接的时候,strip掉调试符号 | | all | 链接的时候,strip掉所有符号,包括调试符号 | 这个api一般在release模式下使用,可以生成更小的二进制程序。。 ```lua target("xxxx") set_strip("all") ``` :::tip 注意 这个api不一定非得在target之后使用,如果没有target指定,那么将会设置到全局模式。。 ::: ## set\_enabled ### 设置是否启用或禁用目标 #### 函数原型 ::: tip API ```lua set_enabled(enabled: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | enabled | 是否启用目标,true表示启用,false表示禁用 | #### 用法说明 如果设置`set_enabled(false)`,则会直接禁用对应的target,包括target的加载和信息获取,而[set\_default](#set_default)仅仅只是设置默认不去编译,但是target还是能获取到相关信息的,默认也会被加载。 ## set\_default ### 设置是否为默认构建安装目标 #### 函数原型 ::: tip API ```lua set_default(default: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | default | 是否作为默认构建目标,true表示默认构建,false表示不默认构建 | #### 用法说明 这个接口用于设置给定工程目标是否作为默认构建,如果没有调用此接口进行设置,那么这个目标就是默认被构建的,例如: ```lua target("test1") set_default(false) target("test2") set_default(true) target("test3") ... ``` 上述代码的三个目标,在执行`xmake`, `xmake install`, `xmake package`, `xmake run`等命令的时候,如果不指定目标名,那么: | 目标名 | 行为 | | ------ | -------------------------------- | | test1 | 不会被默认构建、安装、打包和运行 | | test2 | 默认构建、安装、打包和运行 | | test3 | 默认构建、安装、打包和运行 | 通过上面的例子,可以看到默认目标可以设置多个,运行的时候也会依次运行。 :::tip 注意 需要注意的是,`xmake uninstall`和`xmake clean`命令不受此接口设置影响,因为用户大部分情况下都是喜欢清除和卸载所有。 ::: 如果不想使用默认的目标,那么可以手动指定需要构建安装的目标: ```sh $ xmake build targetname $ xmake install targetname ``` 如果要强制构建安装所有目标,可以传入`[-a|--all]`参数: ```sh $ xmake build [-a|--all] $ xmake install [-a|--all] ``` ## set\_options ### 设置关联选项 #### 函数原型 ::: tip API ```lua set_options(options: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | options | 选项名称字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个选项名称字符串 | #### 用法说明 添加选项依赖,如果通过[option](/zh/api/description/configuration-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") ``` :::tip WARN 只有调用`set_options`进行关联生效后,[option](/zh/api/description/configuration-option#option) 中定义的一些设置才会影响到此`target`目标,例如:宏定义、链接库、编译选项等等 ::: ## set\_symbols ### 设置符号信息 #### 函数原型 ::: tip API ```lua set_symbols(symbols: , { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | symbols | 符号模式字符串,可选值:debug、hidden、none | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 设置目标的符号模式,如果当前没有定义target,那么将会设置到全局状态中,影响所有后续的目标。 目前主要支持一下几个级别: | 值 | 描述 | gcc/clang | msvc | | ------ | ---------------------- | ----- | ---- | | debug | 添加调试符号 | -g | /Zi /Pdxxx.pdb | | debug, edit | 仅 msvc 生效,配合debug级别使用 | 忽略 | /ZI /Pdxxx.pdb | | debug, embed | 仅 msvc 生效,配合debug级别使用 | 忽略 | /Z7 | | hidden | 设置符号不可见 | -fvisibility=hidden | 忽略 | 这两个值也可以同时被设置,例如: ```lua -- 添加调试符号, 设置符号不可见 set_symbols("debug", "hidden") ``` 如果没有调用这个api,默认是禁用调试符号的。。 :::tip 注意 在v2.3.3以上版本,通过跟`set_strip("all")`配合同时设置,可以自动生成独立的调试符号,例如对于ios程序,就是.dSYM文件,对于android等其他程序,就是.sym符号文件。 ::: 如果target同时设置了下面两个设置,就会启用符号文件生成 ```lua target("test") set_symbols("debug") set_strip("all") ``` 对于内置的release模式,默认不启用符号生成,仅仅只是strip targetfile,如果要启用,只需要再额外开启debug符号就行,因为mode.release内部默认已经启用了strip了。 ```lua add_rules("mode.release") target("test") set_symbols("debug") ``` ios程序会生成.dSYM文件,然后同时Strip自身符号 ```sh [ 62%]: linking.release libtest.dylib [ 62%]: generating.release test.dSYM ``` android程序会生成.sym文件(其实就是带符号的so/binary程序),然后同时Strip自身符号 ```sh [ 62%]: linking.release libtest.so [ 62%]: generating.release test.sym ``` v2.3.9 以上版本,新增了 `edit` 和 `embed` 两个额外的附属级别,需要组合 `debug` 级别一起使用,仅用于进一步细分 msvc 编译器的调试符号格式,例如: ```lua set_symbols("debug", "edit") ``` 会从默认的 `-Zi -Pdxxx.pdb` 切换到 `-ZI -Pdxxx.pdb` 编译选项,开启 `Edit and Continue` 调试符号格式信息,当然这并不会影响 gcc/clang 的处理,所以也是完全兼容的。 ## set\_basename ### 设置目标文件名 #### 函数原型 ::: tip API ```lua set_basename(basename: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | 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](#set_targetdir)。 或者通过编写自定义脚本,实现更高级的逻辑,具体见:[after\_build](#after_build)和[os.mv](/zh/api/scripts/builtin-modules/os#os-mv)。 ## set\_filename ### 设置目标文件全名 #### 函数原型 ::: tip API ```lua set_filename(filename: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | filename | 目标文件全名字符串,包括前后缀 | #### 用法说明 它跟[set\_basename](#set_basename)的区别在于,[set\_basename](#set_basename)设置名字不带后缀跟前缀,例如:`libtest.a`,basename如果改成test2后就变成了`libtest2.a`。 而filename的修改,是修改整个目标文件名,包括前后缀,例如可以直接把`libtest.a`改成`test.dll`,这个对于[set\_basename](#set_basename)是做不到的。 ## set\_prefixname ### 设置目标文件的前置名 #### 函数原型 ::: tip API ```lua set_prefixname(prefixname: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | prefixname | 目标文件前置名字符串,如 "lib" 或 "" | #### 用法说明 2.5.5 之后版本才支持,可以修改设置目标文件的前置名,例如将默认的:`libtest.so` 改成 `test.so` ```lua target("test") set_prefixname("") ``` ## set\_suffixname ### 设置目标文件的后置名 #### 函数原型 ::: tip API ```lua set_suffixname(suffixname: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | suffixname | 目标文件后置名字符串,如 "-d" 或 "" | #### 用法说明 2.5.5 之后版本才支持,可以修改设置目标文件的后置名,例如将默认的:`libtest.so` 改成 `libtest-d.so` ```lua target("test") set_suffixname("-d") ``` ## set\_extension ### 设置目标文件的扩展名 #### 函数原型 ::: tip API ```lua set_extension(extension: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | extension | 目标文件扩展名字符串,如 ".dll" 或 ".so" | #### 用法说明 2.5.5 之后版本才支持,可以修改设置目标文件的扩展名,例如将默认的:`libtest.so` 改成 `test.dll` ```lua target("test") set_prefixname("") set_extension(".dll") ``` ## set\_warnings ### 设置警告级别 #### 函数原型 ::: tip API ```lua set_warnings(warnings: , { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | warnings | 警告级别字符串,可选值:none、less、more、all、error | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 设置当前目标的编译的警告级别,一般支持一下几个级别: | 值 | 描述 | gcc/clang | msvc | | ----- | ---------------------- | ---------- | ----------------------------- | | none | 禁用所有警告 | -w | -W0 | | less | 启用较少的警告 | -W1 | -W1 | | more | 启用较多的警告 | -W3 | -W3 | | extra | 启用额外警告 | -Wextra | | | pedantic | 启用非语言标准的使用警告| -Wpedantic | | | all | 启用所有警告 | -Wall | -W3 | | allextra | 启用所有警告+额外的警告 | -Wall -Wextra | -W4 | | everything | 启用全部支持的警告 | -Wall -Wextra -Weffc++ / -Weverything | -Wall | | error | 将所有警告作为编译错误 | -Werror | -WX | 这个api的参数是可以混合添加的,例如: ```lua -- 启用所有警告,并且作为编译错误处理 set_warnings("all", "error") ``` 如果当前没有目标,调用这个api将会设置到全局模式。。 ## set\_optimize ### 设置优化级别 #### 函数原型 ::: tip API ```lua set_optimize(optimize: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | optimize | 优化级别字符串,可选值:none、fast、faster、fastest、smallest、aggressive | #### 用法说明 设置目标的编译优化等级,如果当前没有设置目标,那么将会设置到全局状态中,影响所有后续的目标。 目前主要支持一下几个级别: | 值 | 描述 | gcc/clang | msvc | | ---------- | ---------------------- | ---------- | ------------ | | none | 禁用优化 | -O0 | -Od | | fast | 快速优化 | -O1 | default | | faster | 更快的优化 | -O2 | -O2 | | fastest | 最快运行速度的优化 | -O3 | -Ox -fp:fast | | smallest | 最小化代码优化 | -Os | -O1 -GL | | aggressive | 过度优化 | -Ofast | -Ox -fp:fast | 例如: ```lua -- 最快运行速度的优化 set_optimize("fastest") ``` ## set\_languages ### 设置代码语言标准 #### 函数原型 ::: tip API ```lua set_languages(languages: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | languages | 语言标准字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个语言标准字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 设置目标代码编译的语言标准,如果当前没有目标存在,将会设置到全局模式中。。。 支持的语言标准目前主要有以下几个: | 值 | 描述 | | ---------- | ---------------------- | | ansi | c语言标准: ansi | | c89 | c语言标准: c89 | | gnu89 | c语言标准: gnu89 | | c99 | c语言标准: c99 | | gnu99 | c语言标准: gnu99 | | c11 | c语言标准: c11 | | c17 | c语言标准: c17 | | clatest | c语言标准: clatest | | 值 | 描述 | | ---------- | ---------------------- | | 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` | | cxx20 | c++语言标准: `c++20` | | gnuxx20 | c++语言标准: `gnu++20` | | cxxlatest | c++语言标准: `c++latest` | | gnuxxlatest | c++语言标准: `gnu++latest` | c标准和c++标准可同时进行设置,例如: ```lua -- 设置c代码标准:c99, c++代码标准:c++11 set_languages("c99", "cxx11") ``` 并不是设置了指定的标准,编译器就一定会按这个标准来编译,毕竟每个编译器支持的力度不一样,但是xmake会尽最大可能的去适配当前编译工具的支持标准。 msvc 的编译器并不支持按 c99 的标准来编译c代码,只能支持到c89,但是xmake为了尽可能的支持它,所以在设置c99的标准后,xmake会强制按c++代码模式去编译c代码,从一定程度上解决了windows下编译c99的c代码问题。。 用户不需要去额外做任何修改。 不过最新的 msvc 编译已经支持上了 c11/c17 标准,xmake 也就不会再做额外的特殊处理。 ## set\_fpmodels ### 设置float-point编译模式 #### 函数原型 ::: tip API ```lua set_fpmodels(fpmodels: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | fpmodels | 浮点模式字符串或数组,可选值:fast、strict、except、precise | | ... | 可变参数,可传入多个浮点模式字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 此接口用于设置浮点的编译模式,对数学计算相关优化的编译抽象设置,提供:fast, strict, except, precise 等几种常用的级别,有些可同时设置,有些是有冲突的,最后设置的生效。 关于这些级别的说明,可以参考下微软的文档:[Specify floating-point behavior](https://docs.microsoft.com/en-us/cpp/build/reference/fp-specify-floating-point-behavior?view=vs-2019) 当然,对应gcc/icc等其他编译器,xmake 会映射到不同的编译flags。 ```lua set_fpmodels("fast") set_fpmodels("strict") set_fpmodels("fast", "except") set_fpmodels("precise") -- default ``` 关于这块详情见: ## set\_targetdir ### 设置生成目标文件目录 #### 函数原型 ::: tip API ```lua set_targetdir(targetdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | targetdir | 目标文件输出目录路径字符串 | #### 用法说明 设置目标程序文件的输出目录,一般情况下,不需要设置,默认会输出在build目录下 而build的目录可以在工程配置的时候,手动修改: ```sh xmake f -o /tmp/build ``` 修改成`/tmp/build`后,目标文件默认输出到`/tmp/build`下面。 而如果用这个接口去设置,就不需要每次敲命令修改了,例如: ```lua target("test") set_targetdir("/tmp/build") ``` :::tip 注意 如果显示设置了`set_targetdir`, 那么优先选择`set_targetdir`指定的目录为目标文件的输出目录。 ::: 从 3.0 开始,我们还可以配置 bindir, libdir, includedir 等构建输出的子目录,例如: ```lua target("test") set_kind("shared") add_files("src/x.cpp") set_targetdir("$(builddir)/out", { bindir = "bin", libdir = "lib" }) ``` ## set\_objectdir ### 设置对象文件生成目录 #### 函数原型 ::: tip API ```lua set_objectdir(objectdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | objectdir | 对象文件输出目录路径字符串 | #### 用法说明 设置目标target的对象文件(`*.o/obj`)的输出目录,例如: ```lua target("test") set_objectdir("$(builddir)/.objs") ``` ## set\_dependir ### 设置依赖文件生成目录 #### 函数原型 ::: tip API ```lua set_dependir(dependir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dependir | 依赖文件输出目录路径字符串 | #### 用法说明 设置目标target的编译依赖文件(`.deps`)的输出目录,例如: ```lua target("test") set_dependir("$(builddir)/.deps") ``` ## add\_imports ### 为自定义脚本预先导入扩展模块 #### 函数原型 ::: tip API ```lua add_imports(imports: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | imports | 模块名称字符串或数组,如 "core.base.task" | | ... | 可变参数,可传入多个模块名称字符串 | #### 用法说明 通常,我们在[on\_build](#on_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) ``` ## add\_rules ### 添加规则到目标 #### 函数原型 ::: tip API ```lua add_rules(rules: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | rules | 规则名称字符串或数组,如 "markdown" | | ... | 可变参数,可传入多个规则名称字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 我们可以通过预先设置规则支持的文件后缀,来扩展其他文件的构建支持: ```lua -- 定义一个markdown文件的构建规则 rule("markdown") set_extensions(".md", ".markdown") on_build(function (target, sourcefile) os.cp(sourcefile, path.join(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\_rules时传参: ```lua rule("my_rule") on_load(function (target) local my_arg = extraconf("rules", "my_rule", "my_arg") -- "my arg" end) target("test") add_rules("my_rule", { my_arg = "my arg"}) ``` 我们也可以指定应用局部文件到规则,具体使用见:[add\_files](#add_files)。 ## on\_load ### 自定义目标加载脚本 #### 函数原型 ::: tip API ```lua on_load(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 加载脚本函数,接收 target 参数 | #### 用法说明 在target初始化加载的时候,将会执行此脚本,在里面可以做一些动态的目标配置,实现更灵活的目标描述定义,例如: ```lua target("test") on_load(function (target) add("defines", "DEBUG", "TEST=\"hello\"") add("linkdirs", "/usr/lib", "/usr/local/lib") add({includedirs = "/usr/include", "links" = "pthread"}) end) ``` 可以在`on_load`里面,通过`set`, `add` 来动态添加各种target属性。 ## on\_config ### 自定义配置脚本 #### 函数原型 ::: tip API ```lua on_config(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 配置脚本函数,接收 target 参数 | #### 用法说明 在 `xmake config` 执行完成后,Build 之前会执行此脚本,通常用于编译前的配置工作。它与 on\_load 不同的是,on\_load 只要 target 被加载就会执行,执行时机更早。 如果一些配置,无法在 on\_load 中过早配置,那么都可以在 on\_config 中去配置它。 另外,它的执行时机比 before\_build 还要早,大概的执行流程如下: ``` on_load -> after_load -> on_config -> before_build -> on_build -> after_build ``` ## on\_prepare ### 自定义准备阶段脚本 #### 函数原型 ::: tip API ```lua on_prepare(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 准备阶段脚本函数,接收 target, opt 参数 | #### 用法说明 3.0 新增了 on\_prepare 阶段,实现二阶段编译。prepare 阶段会专门用于处理各种源码级的预处理、codegen 以及源码依赖分析,后续才会进入 build 阶段。 on\_prepare 脚本会在所有 build 脚本之前被调用,我们可以在这里做一些准备工作,比如扫描 C++ module 源文件、自动生成代码等。 ```lua rule("scan_module_files") on_prepare(function (target, opt) -- 扫描模块文件 end) ``` on\_prepare 还支持 jobgraph 参数,可以实现并行任务调度: ```lua rule("scan_module_files") on_prepare(function (target, jobgraph, opt) jobgraph:add(target:name() .. "/scanfiles", function (index, total, opt) -- 扫描模块文件 end) end, {jobgraph = true}) ``` ## on\_prepare\_file ### 自定义准备阶段单文件处理脚本 #### 函数原型 ::: tip API ```lua on_prepare_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 单文件处理脚本函数,接收 target, sourcefile, opt 参数 | #### 用法说明 通过此接口,可以 hook 内置的准备阶段流程,在 prepare 阶段对每个源文件进行预处理、分析、自动生成等操作。 ```lua target("test") set_kind("binary") add_files("src/*.c") on_prepare_file(function (target, sourcefile, opt) -- 处理单个源文件 end) ``` ## on\_prepare\_files ### 自定义准备阶段批量文件处理脚本 #### 函数原型 ::: tip API ```lua on_prepare_files(script: , {jobgraph = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 批量文件处理脚本函数,接收 target, jobgraph, sourcebatch, opt 参数 | | jobgraph | 是否启用并行任务处理,可选值:true、false | #### 用法说明 通过此接口,可以 hook 内置的准备阶段流程,在 prepare 阶段对一批同类型源文件进行批量预处理、分析、自动生成等操作,支持 jobgraph 并行任务。 通常结合 set\_extensions 使用,针对特定类型文件批量处理: ```lua rule("scan_module_files") set_extensions("*.mpp") on_prepare_files(function (target, jobgraph, sourcebatch, opt) jobgraph:add(target:name() .. "/scanfiles", function (index, total, opt) -- 批量扫描模块文件 end) end, {jobgraph = true}) ``` 参数说明: * `target`:当前目标对象 * `jobgraph`:任务调度对象(可选,需 {jobgraph = true}) * `sourcebatch`:同类型源文件批次对象,sourcebatch.sourcefiles() 获取文件列表 * `opt`:可选参数 ## on\_link ### 自定义链接脚本 #### 函数原型 ::: tip API ```lua on_link(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 链接脚本函数,接收 target 参数 | #### 用法说明 这个是在v2.2.7之后新加的接口,用于定制化处理target的链接过程。 ```lua target("test") on_link(function (target) print("link it") end) ``` ## on\_build ### 自定义编译脚本 #### 函数原型 ::: tip API ```lua on_build(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译脚本函数,接收 target 参数 | #### 用法说明 覆盖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) ``` :::tip 注意 一旦对这个target目标设置了自己的build过程,那么xmake默认的构建过程将不再被执行。 ::: ## on\_build\_file ### 自定义编译脚本, 实现单文件构建 #### 函数原型 ::: tip API ```lua on_build_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 单文件编译脚本函数,接收 target, sourcefile, opt 参数 | #### 用法说明 通过此接口,可以用来hook指定target内置的构建过程,替换每个源文件编译过程: ```lua target("test") set_kind("binary") add_files("src/*.c") on_build_file(function (target, sourcefile, opt) end) ``` 如果不想重写内置的编译脚本,仅仅只是在编译前后添加一些自己的处理,其实用:[target.before\_build\_file](#before_build_file)和[target.after\_build\_file](#after_build_file)会更加方便,不需要调用`opt.origin`。 ## on\_build\_files ### 自定义编译脚本, 实现多文件构建 #### 函数原型 ::: tip API ```lua on_build_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 多文件编译脚本函数,接收 target, sourcebatch, opt 参数 | #### 用法说明 通过此接口,可以用来hook指定target内置的构建过程,替换一批同类型源文件编译过程: ```lua target("test") set_kind("binary") add_files("src/*.c") on_build_files(function (target, sourcebatch, opt) end) ``` 设置此接口后,对应源文件列表中文件,就不会出现在自定义的[target.on\_build\_file](#on_build_file)了,因为这个是包含关系。 其中sourcebatch描述了这批同类型源文件: * `sourcebatch.sourcekind`: 获取这批源文件的类型,比如:cc, as, .. * `sourcebatch.sourcefiles()`: 获取源文件列表 * `sourcebatch.objectfiles()`: 获取对象文件列表 * `sourcebatch.dependfiles()`: 获取对应依赖文件列表,存有源文件中编译依赖信息,例如:xxx.d ## on\_clean ### 自定义清理脚本 #### 函数原型 ::: tip API ```lua on_clean(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 清理脚本函数,接收 target 参数 | #### 用法说明 覆盖target目标的`xmake [c|clean}`的清理操作,实现自定义清理过程。 ```lua target("test") -- 设置自定义清理脚本 on_clean(function (target) -- 仅删掉目标文件 os.rm(targetfile()) end) ``` 一些target接口描述如下: | target接口 | 描述 | | ----------------------------------- | ---------------------------------------------------------------- | | name() | 获取目标名 | | targetfile() | 获取目标文件路径 | | get("kind") | 获取目标的构建类型 | | get("defines") | 获取目标的宏定义 | | get("xxx") | 其他通过 `set_/add_`接口设置的target信息,都可以通过此接口来获取 | | add("links", "pthread") | 添加目标设置 | | set("links", "pthread", "z") | 覆写目标设置 | | deps() | 获取目标的所有依赖目标 | | dep("depname") | 获取指定的依赖目标 | | sourcebatches() | 获取目标的所有源文件列表 | ## on\_package ### 自定义打包脚本 #### 函数原型 ::: tip API ```lua on_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包脚本函数,接收 target 参数 | #### 用法说明 覆盖target目标的`xmake [p|package}`的打包操作,实现自定义打包过程,如果你想对指定target打包成自己想要的格式,可以通过这个接口自定义它。 这个接口还是挺实用的,例如,编译完jni后,将生成的so,打包进apk包中。 ```lua -- 定义一个android app的测试demo target("demo") -- 生成动态库:libdemo.so set_kind("shared") -- 设置对象的输出目录,可选 set_objectdir("$(builddir)/.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) ``` ## on\_install ### 自定义安装脚本 #### 函数原型 ::: tip API ```lua on_install(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装脚本函数,接收 target 参数 | #### 用法说明 覆盖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) ``` ## on\_uninstall ### 自定义卸载脚本 #### 函数原型 ::: tip API ```lua on_uninstall(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载脚本函数,接收 target 参数 | #### 用法说明 覆盖target目标的`xmake [u|uninstall}`的卸载操作,实现自定义卸载过程。 ```lua target("test") on_uninstall(function (target) ... end) ``` ## on\_run ### 自定义运行脚本 #### 函数原型 ::: tip API ```lua on_run(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 运行脚本函数,接收 target 参数 | #### 用法说明 覆盖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) ``` ## before\_prepare ### 在准备阶段之前执行自定义脚本 #### 函数原型 ::: tip API ```lua before_prepare(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 准备前脚本函数,接收 target 参数 | #### 用法说明 不会覆盖默认的准备操作,只是在准备阶段之前增加一些自定义操作。 ```lua target("test") before_prepare(function (target) print("before prepare") end) ``` ## before\_prepare\_file ### 在准备阶段单文件处理前执行自定义脚本 #### 函数原型 ::: tip API ```lua before_prepare_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 单文件准备前脚本函数,接收 target, sourcefile, opt 参数 | #### 用法说明 不会覆盖默认的单文件处理操作,只是在 on\_prepare\_file 之前增加一些自定义操作。 ```lua target("test") before_prepare_file(function (target, sourcefile, opt) print("before prepare file") end) ``` ## before\_prepare\_files ### 在准备阶段批量文件处理前执行自定义脚本 #### 函数原型 ::: tip API ```lua before_prepare_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 批量文件准备前脚本函数,接收 target, sourcebatch, opt 参数 | #### 用法说明 不会覆盖默认的批量处理操作,只是在 on\_prepare\_files 之前增加一些自定义操作。 ```lua target("test") before_prepare_files(function (target, sourcebatch, opt) print("before prepare files") end) ``` ## before\_link ### 在链接之前执行一些自定义脚本 #### 函数原型 ::: tip API ```lua before_link(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 链接前脚本函数,接收 target 参数 | #### 用法说明 这个是在v2.2.7之后新加的接口,用于在链接之前增加一些自定义的操作。 ```lua target("test") before_link(function (target) print("") end) ``` ## before\_build ### 在构建之前执行一些自定义脚本 #### 函数原型 ::: tip API ```lua before_build(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 构建前脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的构建操作,只是在构建之前增加一些自定义的操作。 ```lua target("test") before_build(function (target) print("") end) ``` ## before\_build\_file ### 自定义编译前的脚本, 实现单文件构建 #### 函数原型 ::: tip API ```lua before_build_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 单文件构建前脚本函数,接收 target, sourcefile, opt 参数 | #### 用法说明 通过此接口,可以用来hook指定target内置的构建过程,在每个源文件编译过程之前执行一些自定义脚本: ```lua target("test") set_kind("binary") add_files("src/*.c") before_build_file(function (target, sourcefile, opt) end) ``` ## before\_build\_files ### 自定义编译前的脚本, 实现多文件构建 #### 函数原型 ::: tip API ```lua before_build_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 多文件构建前脚本函数,接收 target, sourcebatch, opt 参数 | #### 用法说明 通过此接口,可以用来hook指定target内置的构建过程,在一批同类型源文件编译过程之前执行一些自定义脚本: ```lua target("test") set_kind("binary") add_files("src/*.c") before_build_files(function (target, sourcebatch, opt) end) ``` ## before\_clean ### 在清理之前执行一些自定义脚本 #### 函数原型 ::: tip API ```lua before_clean(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 清理前脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的清理操作,只是在清理之前增加一些自定义的操作。 ```lua target("test") before_clean(function (target) print("") end) ``` ## before\_package ### 在打包之前执行一些自定义脚本 #### 函数原型 ::: tip API ```lua before_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包前脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的打包操作,只是在打包之前增加一些自定义的操作。 ```lua target("test") before_package(function (target) print("") end) ``` ## before\_install ### 在安装之前执行一些自定义脚本 #### 函数原型 ::: tip API ```lua before_install(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装前脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的安装操作,只是在安装之前增加一些自定义的操作。 ```lua target("test") before_install(function (target) print("") end) ``` ## before\_uninstall ### 在卸载之前执行一些自定义脚本 #### 函数原型 ::: tip API ```lua before_uninstall(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载前脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的卸载操作,只是在卸载之前增加一些自定义的操作。 ```lua target("test") before_uninstall(function (target) print("") end) ``` ## before\_run ### 在运行之前执行一些自定义脚本 #### 函数原型 ::: tip API ```lua before_run(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 运行前脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的运行操作,只是在运行之前增加一些自定义的操作。 ```lua target("test") before_run(function (target) print("") end) ``` ## after\_prepare ### 在准备阶段之后执行自定义脚本 #### 函数原型 ::: tip API ```lua after_prepare(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 准备后脚本函数,接收 target 参数 | #### 用法说明 不会覆盖默认的准备操作,只是在准备阶段之后增加一些自定义操作。 ```lua target("test") after_prepare(function (target) print("after prepare") end) ``` ## after\_prepare\_file ### 在准备阶段单文件处理后执行自定义脚本 #### 函数原型 ::: tip API ```lua after_prepare_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 单文件准备后脚本函数,接收 target, sourcefile, opt 参数 | #### 用法说明 不会覆盖默认的单文件处理操作,只是在 on\_prepare\_file 之后增加一些自定义操作。 ```lua target("test") after_prepare_file(function (target, sourcefile, opt) print("after prepare file") end) ``` ## after\_prepare\_files ### 在准备阶段批量文件处理后执行自定义脚本 #### 函数原型 ::: tip API ```lua after_prepare_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 批量文件准备后脚本函数,接收 target, sourcebatch, opt 参数 | #### 用法说明 不会覆盖默认的批量处理操作,只是在 on\_prepare\_files 之后增加一些自定义操作。 ```lua target("test") after_prepare_files(function (target, sourcebatch, opt) print("after prepare files") end) ``` ## after\_link ### 在链接之后执行一些自定义脚本 #### 函数原型 ::: tip API ```lua after_link(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 链接后脚本函数,接收 target 参数 | #### 用法说明 这个是在v2.2.7之后新加的接口,用于在链接之后增加一些自定义的操作。 ```lua target("test") after_link(function (target) print("") end) ``` ## after\_build ### 在构建之后执行一些自定义脚本 #### 函数原型 ::: tip API ```lua after_build(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 构建后脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的构建操作,只是在构建之后增加一些自定义的操作。 例如,对于ios的越狱开发,构建完程序后,需要用`ldid`进行签名操作 ```lua target("test") after_build(function (target) os.run("ldid -S %s", targetfile()) end) ``` ## after\_build\_file ### 自定义编译前的脚本, 实现单文件构建 #### 函数原型 ::: tip API ```lua after_build_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 单文件构建后脚本函数,接收 target, sourcefile, opt 参数 | #### 用法说明 通过此接口,可以用来hook指定target内置的构建过程,在每个源文件编译过程之后执行一些自定义脚本: ```lua target("test") set_kind("binary") add_files("src/*.c") after_build_file(function (target, sourcefile, opt) end) ``` ## after\_build\_files ### 自定义编译前的脚本, 实现多文件构建 #### 函数原型 ::: tip API ```lua after_build_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 多文件构建后脚本函数,接收 target, sourcebatch, opt 参数 | #### 用法说明 通过此接口,可以用来hook指定target内置的构建过程,在一批同类型源文件编译过程之后执行一些自定义脚本: ```lua target("test") set_kind("binary") add_files("src/*.c") after_build_files(function (target, sourcebatch, opt) end) ``` ## after\_clean ### 在清理之后执行一些自定义脚本 #### 函数原型 ::: tip API ```lua after_clean(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 清理后脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的清理操作,只是在清理之后增加一些自定义的操作。 一般可用于清理编译某target自动生成的一些额外的临时文件,这些文件xmake默认的清理规则可能没有清理到,例如: ```lua target("test") after_clean(function (target) os.rm("$(builddir)/otherfiles") end) ``` ## after\_package ### 在打包之后执行一些自定义脚本 #### 函数原型 ::: tip API ```lua after_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包后脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的打包操作,只是在打包之后增加一些自定义的操作。 ```lua target("test") after_package(function (target) print("") end) ``` ## after\_install ### 在安装之后执行一些自定义脚本 #### 函数原型 ::: tip API ```lua after_install(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装后脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的安装操作,只是在安装之后增加一些自定义的操作。 ```lua target("test") after_install(function (target) print("") end) ``` ## after\_uninstall ### 在卸载之后执行一些自定义脚本 #### 函数原型 ::: tip API ```lua after_uninstall(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载后脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的卸载操作,只是在卸载之后增加一些自定义的操作。 ```lua target("test") after_uninstall(function (target) print("") end) ``` ## after\_run ### 在运行之后执行一些自定义脚本 #### 函数原型 ::: tip API ```lua after_run(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 运行后脚本函数,接收 target 参数 | #### 用法说明 并不会覆盖默认的运行操作,只是在运行之后增加一些自定义的操作。 ```lua target("test") after_run(function (target) print("") end) ``` ## set\_pcheader ### 设置 C 预编译头文件 #### 函数原型 ::: tip API ```lua set_pcheader(header: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | header | C预编译头文件路径字符串 | #### 用法说明 xmake支持通过预编译头文件去加速c程序编译,目前支持的编译器有:gcc, clang和msvc。 使用方式如下: ```lua target("test") set_pcheader("header.h") ``` ## set\_pcxxheader ### 设置 C++ 预编译头文件 #### 函数原型 ::: tip API ```lua set_pcxxheader(header: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | header | C++预编译头文件路径字符串 | #### 用法说明 xmake支持通过预编译头文件去加速c++程序编译,目前支持的编译器有:gcc, clang和msvc。 使用方式如下: ```lua target("test") set_pcxxheader("header.h") ``` ## set\_pmheader ### 设置 ObjC 预编译头文件 #### 函数原型 ::: tip API ```lua set_pmheader(header: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | header | ObjC预编译头文件路径字符串 | #### 用法说明 xmake支持通过预编译头文件去加速 ObjC 程序编译,目前支持的编译器有:gcc, clang和msvc。 使用方式如下: ```lua target("test") set_pmheader("header.h") ``` ## set\_pmxxheader ### 设置 ObjC++ 预编译头文件 #### 函数原型 ::: tip API ```lua set_pmxxheader(header: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | header | ObjC++预编译头文件路径字符串 | #### 用法说明 xmake支持通过预编译头文件去加速 ObjC++ 程序编译,目前支持的编译器有:gcc, clang和msvc。 使用方式如下: ```lua target("test") set_pmxxheader("header.h") ``` ## add\_deps ### 添加子工程目标依赖 #### 函数原型 ::: tip API ```lua add_deps(deps: , ..., { inherit = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | deps | 依赖目标名称字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个依赖目标名称字符串 | | inherit | 是否继承依赖目标的配置,可选值:true(继承)、false(不继承) | #### 用法说明 添加当前目标的依赖目标,编译的时候,会去优先编译依赖的目标,然后再编译当前目标。。。 ```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会去用到他们 :::tip 注意 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 ## add\_links ### 添加链接库名 #### 函数原型 ::: tip API ```lua add_links(links: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | links | 链接库名称字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个链接库名称字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 为当前目标添加链接库,一般这个要与[add\_linkdirs](#add_linkdirs)配对使用。 ```lua target("demo") -- 添加对libtest.a的链接,相当于 -ltest add_links("test") -- 添加链接搜索目录 add_linkdirs("$(builddir)/lib") ``` 2.8.1 版本开始,add\_links 还支持添加库的完整路径,例如:`add_links("/tmp/libfoo.a")`,显式的指定库文件。 ## add\_syslinks ### 添加系统链接库名 #### 函数原型 ::: tip API ```lua add_syslinks(syslinks: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | syslinks | 系统链接库名称字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个系统链接库名称字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 这个接口使用上跟[add\_links](#add_links)类似,唯一的区别就是,通过这个接口添加的链接库顺序在所有`add_links`之后。 因此主要用于添加系统库依赖,因为系统库的链接顺序是非常靠后的,例如: ```lua add_syslinks("pthread", "m", "dl") target("demo") add_links("a", "b") add_linkdirs("$(builddir)/lib") ``` 上面的配置,即使`add_syslinks`被优先提前设置了,但最后的链接顺序依然是:`-la -lb -lpthread -lm -ldl` ## add\_linkorders ### 调整链接顺序 #### 函数原型 ::: tip API ```lua add_linkorders(linkorders: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | linkorders | 链接顺序字符串或数组,如 "dep1", "dep2" | | ... | 可变参数,可传入多个链接顺序字符串 | #### 用法说明 这是 xmake 2.8.5 以后的版本才支持的特性,主要用于调整 target 内部的链接顺序。 由于 xmake 提供了 `add_links`, `add_deps`, `add_packages`, `add_options` 接口,可以配置目标、依赖,包和选项中的链接。 但是它们之间的链接顺序,在之前可控性比较弱,只能按固定顺序生成,这对于一些复杂的项目,就有点显得力不从心了。 更多详情和背景见:[#1452](https://github.com/xmake-io/xmake/issues/1452) #### 排序链接 为了更加灵活的调整 target 内部的各种链接顺序,我们新增了 `add_linkorders` 接口,用于配置目标、依赖、包、选项、链接组引入的各种链接顺序。 例如: ```lua add_links("a", "b", "c", "d", "e") -- e -> b -> a add_linkorders("e", "b", "a") -- e -> d add_linkorders("e", "d") ``` add\_links 是配置的初始链接顺序,然后我们通过 add\_linkorders 配置了两个局部链接依赖 `e -> b -> a` 和 `e -> d` 后。 xmake 内部就会根据这些配置,生成 DAG 图,通过拓扑排序的方式,生成最终的链接顺序,提供给链接器。 当然,如果存在循环依赖,产生了环,它也会提供警告信息。 #### 排序链接和链接组 另外,对于循环依赖,我们也可以通过 `add_linkgroups` 配置链接组的方式也解决。 并且 `add_linkorders` 也能够对链接组进行排序。 ```lua add_links("a", "b", "c", "d", "e") add_linkgroups("c", "d", {name = "foo", group = true}) add_linkorders("e", "linkgroup::foo") ``` 如果要排序链接组,我们需要对每个链接组取个名,`{name = "foo"}` ,然后就能在 `add_linkorders` 里面通过 `linkgroup::foo` 去引用配置了。 2.9.6 版本新增 as\_needed 配置项,可以用于禁用 as\_needed。(默认不配置,就是开启状态。) ```lua add_linkgroups("c", "d", {as_needed = false}) ``` 对应的 flags 如下。 ```sh -Wl,--no-as-needed c d -Wl,--as-needed ``` #### 排序链接和frameworks 我们也可以排序链接和 macOS/iPhoneOS 的 frameworks。 ```lua add_links("a", "b", "c", "d", "e") add_frameworks("Foundation", "CoreFoundation") add_linkorders("e", "framework::CoreFoundation") ``` #### 完整例子 相关的完整例子,我们可以看下: ```lua add_rules("mode.debug", "mode.release") add_requires("libpng") target("bar") set_kind("shared") add_files("src/foo.cpp") add_linkgroups("m", "pthread", {whole = true}) target("foo") set_kind("static") add_files("src/foo.cpp") add_packages("libpng", {public = true}) target("demo") set_kind("binary") add_deps("foo") add_files("src/main.cpp") if is_plat("linux", "macosx") then add_syslinks("pthread", "m", "dl") end if is_plat("macosx") then add_frameworks("Foundation", "CoreFoundation") end add_linkorders("framework::Foundation", "png16", "foo") add_linkorders("dl", "linkgroup::syslib") add_linkgroups("m", "pthread", {name = "syslib", group = true}) ``` 完整工程在:[linkorders example](https://github.com/xmake-io/xmake/blob/master/tests/projects/c%2B%2B/linkorders/xmake.lua) ## add\_linkgroups ### 添加链接组 #### 函数原型 ::: tip API ```lua add_linkgroups(linkgroups: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | linkgroups | 链接组名称字符串或数组,如 "group1", "group2" | | ... | 可变参数,可传入多个链接组名称字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 这是 xmake 2.8.5 以后的版本才支持的特性,这个链接组的特性,目前主要用于 linux 平台的编译,仅支持 gcc/clang 编译器。 需要注意的是 gcc/clang 里面的链接组概念主要特指:`-Wl,--start-group` 而 xmake 对齐进行了封装,做了进一步抽象,并且不仅仅用于处理 `-Wl,--start-group`,还可以处理 `-Wl,--whole-archive` 和 `-Wl,-Bstatic`。 下面我们会一一对其进行讲解。 更多详情见:[#1452](https://github.com/xmake-io/xmake/issues/1452) #### --start-group 支持 `-Wl,--start-group` 和 `-Wl,--end-group` 是用于处理复杂库依赖关系的链接器选项,确保链接器可以解决符号依赖并成功连接多个库。 在 xmake 中,我们可以通过下面的方式实现: ```lua add_linkgroups("a", "b", {group = true}) ``` 它会对应生成 `-Wl,--start-group -la -lb -Wl,--end-group` 链接选项。 如果 a 和 b 库之间有符号的循环依赖,也不会报链接错误,能够正常链接成功。 对于不支持的平台和编译,会退化成 `-la -lb` #### --whole-archive 支持 `--whole-archive` 是一个链接器选项,通常用于处理静态库。 它的作用是告诉链接器将指定的静态库中的所有目标文件都包含到最终可执行文件中,而不仅仅是满足当前符号依赖的目标文件。 这可以用于确保某些库的所有代码都被链接,即使它们在当前的符号依赖关系中没有直接引用。 更多信息,可以参考 gcc/clang 的文档。 在 xmake 中,我们可以通过下面的方式实现: ```lua add_linkgroups("a", "b", {whole = true}) ``` 它会对应生成 `-Wl,--whole-archive -la -lb -Wl,--no-whole-archive` 链接选项。 对于不支持的平台和编译,会退化成 `-la -lb` 另外,我们可以同时配置 group/whole: ```lua add_linkgroups("a", "b", {whole = true, group = true}) ``` #### -Bstatic 支持 `-Bstatic` 也是用于编译器(如gcc)的选项,用于指示编译器在链接时只使用静态库而不使用共享库。 更多信息,可以参考 gcc/clang 的文档。 在 xmake 中,我们可以通过下面的方式实现: ```lua add_linkgroups("a", "b", {static = true}) ``` 它会对应生成 `-Wl,-Bstatic -la -lb -Wl,-Bdynamic` 链接选项。 ## add\_files ### 添加源代码文件 #### 函数原型 ::: tip API ```lua add_files(files: , ..., { defines = , languages = , includedirs = , rules = , force = {$flags}, sourcekind = , $flags = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | files | 文件路径字符串或文件路径数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个文件路径字符串 | | defines | 宏定义字符串或数组,为指定文件设置编译宏定义 | | languages | 语言标准字符串,如 "c99", "c++11" 等 | | includedirs | 头文件搜索目录字符串或数组 | | rules | 自定义构建规则名称字符串 | | force | 强制编译选项对象,禁用自动检测,可包含各种编译选项 | | sourcekind | 强制指定源文件类型字符串,如 "cc", "cxx" 等 | | $flags | 各种编译和链接选项,包括 cflags, cxflags, cxxflags, mflags, mxflags, mxxflags, scflags, asflags, gcflags, dcflags, rcflags, fcflags, zcflags, cuflags, culdflags, cugencodes, ldflags, arflags, shflags 等 | #### 用法说明 用于添加目标工程的源文件,甚至库文件,目前支持的一些文件类型: | 支持的源文件类型 | 描述 | | ------------------ | ---------------------------------- | | .c/.cpp/.cc/.cxx | c++文件 | | .s/.S/.asm | 汇编文件 | | .m/.mm | objc文件 | | .swift | swift文件 | | .go | golang文件 | | .o/.obj | 对象文件 | | .a/.lib | 静态库文件,会自动合并库到目标程序 | | .rc | msvc的资源文件 | | .manifest | windows manifest 文件 | | .def | windows dll 导出文件 | | .ld/.lds | linker scripts 文件,通常用于 gcc/clang | | .map/.ver | version script 文件,通常用于 gcc/clang | 其中通配符`*`表示匹配当前目录下文件,而`**`则匹配多级目录下的文件。 例如: ```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") ``` 其中分隔符`|`之后的都是需要排除的文件,这些文件也同样支持匹配模式,并且可以同时添加多个过滤模式,只要中间用`|`分割就行了。。 添加文件的时候支持过滤一些文件的一个好处就是,可以为后续根据不同开关逻辑添加文件提供基础。 :::tip 注意 为了使得描述上更加的精简,`|`之后的过滤描述都是基于起一个模式:`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"}}) ``` 2.3.1版本之后,可以通过sourcekind参数强制使用c或c++编译器: ```lua add_files("*.c", {sourcekind = "cxx"}) -- force to compile as c++ add_files("*.cpp", {sourcekind = "cc"}) -- force to compile as c ``` ## remove\_files ### 从前面的源代码文件列表中删除指定文件 #### 函数原型 ::: tip API ```lua remove_files(files: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | files | 文件路径字符串或文件路径数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个文件路径字符串 | #### 用法说明 通过此接口,可以从前面[add\_files](#add_files)接口添加的文件列表中,删除指定的文件,例如: ```lua target("test") add_files("src/*.c") remove_files("src/test.c") ``` 上面的例子,可以从`src`目录下添加除`test.c`以外的所有文件,当然这个也可以通过`add_files("src/*.c|test.c")`来达到相同的目的,但是这种方式更加灵活。 例如,我们可以条件判断来控制删除哪些文件,并且此接口也支持[add\_files](#add_files)的匹配模式,过滤模式,进行批量移除。 ```lua target("test") add_files("src/**.c") remove_files("src/test*.c") remove_files("src/subdir/*.c|xxx.c") if is_plat("iphoneos") then add_files("xxx.m") end ``` 通过上面的例子,我们可以看出`add_files`和`remove_files`是根据调用顺序,进行顺序添加和删除的,并且通过`remove_files("src/subdir/*.c|xxx.c")`删除一批文件, 并且排除`src/subdir/xxx.c`(就是说,不删除这个文件)。 注: 这个接口 v2.6.3 版本才提供,之前的版本是 del\_files,已经废弃。 如果向下要兼容以前的版本,可以通过下面的配置解决。 ```lua remove_files = remove_files or del_files ``` ## remove\_headerfiles ### 从前面的头文件列表中删除指定文件 #### 函数原型 ::: tip API ```lua remove_headerfiles(headerfiles: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | headerfiles | 头文件路径字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个头文件路径字符串 | #### 用法说明 主要用于从 `add_headerfiles` 设置的头文件列表中删除文件,用法与 `remove_files` 类似。 这个接口,v2.6.3 版本才提供。 ## add\_linkdirs ### 添加链接库搜索目录 #### 函数原型 ::: tip API ```lua add_linkdirs(linkdirs: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | linkdirs | 链接库搜索目录字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个链接库搜索目录字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 设置链接库的搜索目录,这个接口的使用方式如下: ```lua target("test") add_linkdirs("$(builddir)/lib") ``` 此接口相当于gcc的`-Lxxx`链接选项。 一般他是与[add\_links](#add_links)配合使用的,当然也可以直接通过[add\_ldflags](#add_ldflags)或者[add\_shflags](#add_shflags)接口来添加,也是可以的。 :::tip 注意 如果不想在工程中写死,可以通过:`xmake f --linkdirs=xxx`或者`xmake f --ldflags="-L/xxx"`的方式来设置,当然这种手动设置的目录搜索优先级更高。 ::: ## add\_rpathdirs ### 添加程序运行时动态库的加载搜索目录 #### 函数原型 ::: tip API ```lua add_rpathdirs(rpathdirs: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | rpathdirs | 运行时库搜索目录字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个运行时库搜索目录字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 通过[add\_linkdirs](#add_linkdirs)设置动态库的链接搜索目录后,程序被正常链接,但是在linux平台想要正常运行编译后的程序,会报加载动态库失败。 因为没找到动态库的加载目录,想要正常运行依赖动态库的程序,需要设置`LD_LIBRARY_PATH`环境变量,指定需要加载的动态库目录。 但是这种方式是全局的,影响太广,更好的方式是通过`-rpath=xxx`的链接器选项,在链接程序的时候设置好需要加载的动态库搜索路径,而xmake对其进行了封装,通过`add_rpathdirs`更好的处理跨平台问题。 具体使用如下: ```lua target("test") set_kind("binary") add_linkdirs("$(builddir)/lib") add_rpathdirs("$(builddir)/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("$(builddir)/lib") add_rpathdirs("@loader_path/lib") ``` 指定test程序加载当前执行目录下`lib/*.[so|dylib]`的动态库文件,这将有助于提升程序的可移植性,不用写死绝对路径和相对路径,导致程序和目录切换引起程序加载动态库失败。 :::tip 注意 需要注意的是,在macos下,要想 add\_rpathdirs 设置生效,需要对dylib做一些预处理,添加`@rpath/xxx`路径设置: ::: `$install_name_tool -add_rpath @rpath/libxxx.dylib xxx/libxxx.dylib` 我们也可以通过`otool -L libxxx.dylib`查看是否存在带@rpath的路径 另外,对于 gcc, `add_rpathdirs` 默认设置的是 runpath,如果想要显式的配置上 `-Wl,--enable-new-dtags`, `-Wl,--disable-new-dtags` 去配置 rpath 还是 runpath 我们可以通过额外的参数指定,`add_rpathdirs("xxx", {runpath = true})` 相关背景细节见:[#5109](https://github.com/xmake-io/xmake/issues/5109) 2.9.4 之后,我们新增了 `add_rpathdirs("xxx", {install_only = true})` ,可以单独配置安装后的 rpath 路径。 ## add\_includedirs ### 添加头文件搜索目录 #### 函数原型 ::: tip API ```lua add_includedirs(includedirs: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includedirs | 头文件搜索目录字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个头文件搜索目录字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 设置头文件的搜索目录,这个接口的使用方式如下: ```lua target("test") add_includedirs("$(builddir)/include") ``` 当然也可以直接通过[add\_cxflags](#add_cxflags)或者[add\_mxflags](#add_mxflags)等接口来设置,也是可以的。 2.2.5之后,可通过额外的`{public|interface = true}`属性设置,将includedirs导出给依赖的子target,例如: ```lua target("test") set_kind("static") add_includedirs("src/include") -- 仅对当前target生效 add_includedirs("$(builddir)/include", {public = true}),当前target和子target都会被设置 target("demo") set_kind("binary") add_deps("test") ``` 更多关于这块的说明,见:[add\_deps](#add_deps) :::tip 注意 如果不想在工程中写死,可以通过:`xmake f --includedirs=xxx`或者`xmake f --cxflags="-I/xxx"`的方式来设置,当然这种手动设置的目录搜索优先级更高。 ::: :::tip 注意 头文件默认不支持模式匹配,也不推荐这么做, 容易引入一些不需要的子目录,导致各种头文件引用冲突干扰,出了问题更难查。 ::: 如果用户非要这么做,可以通过 `add_includedirs(os.dirs(path.join(os.scriptdir(), "xxx/**")))` 来实现。 ## add\_sysincludedirs ### 添加系统头文件搜索目录 #### 函数原型 ::: tip API ```lua add_sysincludedirs(includedirs: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includedirs | 系统头文件搜索目录字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个系统头文件搜索目录字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 `add_includedirs` 通常用于添加工程头文件搜索目录,而一些系统库头文件的引入,有可能会触发一些内部的警告信息,但是这些警告对于用户来讲也许是无法避免,也修复不了的。 那么,每次显示这些警告反而会干扰用户,因此,gcc/clang 提供了 `-isystem` 专门用来设置系统头文件搜索路径,通过此接口设置的头文件,会压制一些警告信息来避免干扰用户。 msvc 也通提供了 `/external:I` 编译选项来设置它,但是需要高版本 msvc 才支持。 因此,xmake 提供了 `add_sysincludedirs` 来抽象适配设置系统库头文件搜索路径,如果当前编译器不支持,会自动切换回 `-I` 编译选项。 ```lua target("test") add_sysincludedirs("/usr/include") ``` 生成的编译选项如下: ```sh -isystem /usr/include ``` 如果是 msvc 编译器,则会是: ```sh /experimental:external /external:W0 /external:I /usr/include ``` :::tip 注意 另外,使用 `add_requires()` 引入的依赖包,默认也会使用 `-isystem` 作为外部系统头文件。 ::: ## add\_defines ### 添加宏定义 #### 函数原型 ::: tip API ```lua add_defines(defines: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | defines | 宏定义字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个宏定义字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 ```lua add_defines("DEBUG", "TEST=0", "TEST2=\"hello\"") ``` 相当于设置了编译选项: ``` -DDEBUG -DTEST=0 -DTEST2=\"hello\" ``` ## add\_undefines ### 取消宏定义 #### 函数原型 ::: tip API ```lua add_undefines(undefines: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | undefines | 宏定义名称字符串或数组,如 "DEBUG" | | ... | 可变参数,可传入多个宏定义名称字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 ```lua add_undefines("DEBUG") ``` 相当于设置了编译选项:`-UDEBUG` 在代码中相当于:`#undef DEBUG` ## add\_cflags ### 添加c编译选项 #### 函数原型 ::: tip API ```lua add_cflags(cflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cflags | C编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个C编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 仅对c代码添加编译选项 ```lua add_cflags("-g", "-O2", "-DDEBUG") ``` :::tip 注意 所有选项值都基于gcc的定义为标准,如果其他编译器不兼容(例如:vc),xmake会自动内部将其转换成对应编译器支持的选项值。 ::: 用户无需操心其兼容性,如果其他编译器没有对应的匹配值,那么xmake会自动忽略器设置。 在2.1.9版本之后,可以通过force参数来强制禁用flags的自动检测,直接传入编译器,哪怕编译器有可能不支持,也会设置: ```lua add_cflags("-g", "-O2", {force = true}) ``` ## add\_cxflags ### 添加c/c++编译选项 #### 函数原型 ::: tip API ```lua add_cxflags(cxflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cxflags | C/C++编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个C/C++编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 同时对c/c++代码添加编译选项,用法跟 add\_cflags 一致。 ## add\_cxxflags ### 添加c++编译选项 #### 函数原型 ::: tip API ```lua add_cxxflags(cxxflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cxxflags | C++编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个C++编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 仅对c++代码添加编译选项,用法跟 add\_cflags 一致。 #### 添加特定编译器 flags 2.7.3 版本中,我们改进了所有 flags 添加接口,可以仅仅对特定编译器指定 flags,例如: ```lua add_cxxflags("clang::-stdlib=libc++") add_cxxflags("gcc::-stdlib=libc++") add_cxxflags("cl::/GR-") add_cxxflags("clang_cl::/GR-") ``` 或者: ```lua add_cxxflags("-stdlib=libc++", {tools = "clang"}) add_cxxflags("-stdlib=libc++", {tools = "gcc"}) add_cxxflags("/GR-", {tools = {"clang_cl", "cl"}}) ``` :::tip 注意 不仅仅是编译flags,对 add\_ldflags 等链接 flags,也是同样生效的。 ::: ## add\_mflags ### 添加objc编译选项 #### 函数原型 ::: tip API ```lua add_mflags(mflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | mflags | ObjC编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个ObjC编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 仅对objc代码添加编译选项 ```lua add_mflags("-g", "-O2", "-DDEBUG") ``` 在2.1.9版本之后,可以通过force参数来强制禁用flags的自动检测,直接传入编译器,哪怕编译器有可能不支持,也会设置: ```lua add_mflags("-g", "-O2", {force = true}) ``` ## add\_mxflags ### 添加objc/objc++编译选项 #### 函数原型 ::: tip API ```lua add_mxflags(mxflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | mxflags | ObjC/ObjC++编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个ObjC/ObjC++编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 同时对objc/objc++代码添加编译选项 ```lua add_mxflags("-framework CoreFoundation") ``` ## add\_mxxflags ### 添加objc++编译选项 #### 函数原型 ::: tip API ```lua add_mxxflags(mxxflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | mxxflags | ObjC++编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个ObjC++编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 仅对objc++代码添加编译选项 ```lua add_mxxflags("-framework CoreFoundation") ``` ## add\_scflags ### 添加swift编译选项 #### 函数原型 ::: tip API ```lua add_scflags(scflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | scflags | Swift编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个Swift编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对swift代码添加编译选项 ```lua add_scflags("xxx") ``` ## add\_asflags ### 添加汇编编译选项 #### 函数原型 ::: tip API ```lua add_asflags(asflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | asflags | 汇编编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个汇编编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对汇编代码添加编译选项 ```lua add_asflags("xxx") ``` ## add\_gcflags ### 添加go编译选项 #### 函数原型 ::: tip API ```lua add_gcflags(gcflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | gcflags | Go编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个Go编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对golang代码添加编译选项 ```lua add_gcflags("xxx") ``` ## add\_dcflags ### 添加dlang编译选项 #### 函数原型 ::: tip API ```lua add_dcflags(dcflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | dcflags | D语言编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个D语言编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对dlang代码添加编译选项 ```lua add_dcflags("xxx") ``` ## add\_rcflags ### 添加rust编译选项 #### 函数原型 ::: tip API ```lua add_rcflags(rcflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | rcflags | Rust编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个Rust编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对rust代码添加编译选项 ```lua add_rcflags("xxx") ``` ## add\_fcflags ### 添加fortran编译选项 #### 函数原型 ::: tip API ```lua add_fcflags(fcflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | fcflags | Fortran编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个Fortran编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对fortran代码添加编译选项 ```lua add_fcflags("xxx") ``` ## add\_zcflags ### 添加zig编译选项 #### 函数原型 ::: tip API ```lua add_zcflags(zcflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | zcflags | Zig编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个Zig编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对zig代码添加编译选项 ```lua add_zcflags("xxx") ``` ## add\_cuflags ### 添加cuda编译选项 #### 函数原型 ::: tip API ```lua add_cuflags(cuflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cuflags | CUDA编译选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个CUDA编译选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对cuda代码添加编译选项 ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") ``` ## add\_culdflags ### 添加cuda设备链接选项 #### 函数原型 ::: tip API ```lua add_culdflags(culdflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | culdflags | CUDA设备链接选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个CUDA设备链接选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 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") ``` ## add\_cugencodes ### 添加cuda设备的gencode设置 #### 函数原型 ::: tip API ```lua add_cugencodes(cugencodes: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | cugencodes | CUDA设备gencode设置字符串或数组,如 "sm\_30", "sm\_50" | | ... | 可变参数,可传入多个CUDA设备gencode设置字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 `add_cugencodes()`接口其实就是对`add_cuflags("-gencode arch=compute_xx,code=compute_xx")`编译flags设置的简化封装,其内部参数值对应的实际flags映射关系如下: ```lua - compute_xx --> `-gencode arch=compute_xx,code=compute_xx` - sm_xx --> `-gencode arch=compute_xx,code=sm_xx` - sm_xx,sm_yy --> `-gencode arch=compute_xx,code=[sm_xx,sm_yy]` - compute_xx,sm_yy --> `-gencode arch=compute_xx,code=sm_yy` - compute_xx,sm_yy,sm_zz --> `-gencode arch=compute_xx,code=[sm_yy,sm_zz]` - native --> match the fastest cuda device on current host, eg. for a Tesla P100, `-gencode arch=compute_60,code=sm_60` will be added, if no available device is found, no `-gencode` flags will be added ``` 例如: ```lua add_cugencodes("sm_30") ``` 就等价为 ```lua add_cuflags("-gencode arch=compute_30,code=sm_30") add_culdflags("-gencode arch=compute_30,code=sm_30") ``` 是不是上面的更加精简些,这其实就是个用于简化设置的辅助接口。 而如果我们设置了native值,那么xmake会自动探测当前主机的cuda设备,然后快速匹配到它对应的gencode设置,自动追加到整个构建过程中。 例如,如果我们主机目前的GPU是Tesla P100,并且能够被xmake自动检测到,那么下面的设置: ```lua add_cugencodes("native") ``` 等价于: ```lua add_cugencodes("sm_60") ``` ## add\_ldflags ### 添加链接选项 #### 函数原型 ::: tip API ```lua add_ldflags(ldflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | ldflags | 链接选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个链接选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 添加静态链接选项 ```lua add_ldflags("-L/xxx", "-lxxx") ``` 在添加链接选项时,默认无法支持参数内有空格,使用expand = false: ```lua -- add_ldflags("-L/my lib") ERROR: Invalid arguments add_ldflags({"-L/my lib"}, {expand = false}) -- OK ``` ## add\_arflags ### 添加静态库归档选项 #### 函数原型 ::: tip API ```lua add_arflags(arflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | arflags | 静态库归档选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个静态库归档选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 影响对静态库的生成 ```lua add_arflags("xxx") ``` ## add\_shflags ### 添加动态库链接选项 #### 函数原型 ::: tip API ```lua add_shflags(shflags: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | shflags | 动态库链接选项字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个动态库链接选项字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 影响对动态库的生成 ```lua add_shflags("xxx") ``` ## add\_options ### 添加关联选项 #### 用法说明 这个接口跟[set\_options](#set_options)类似,唯一的区别就是,此处是追加选项,而[set\_options](#set_options)每次设置会覆盖先前的设置。 ## add\_packages ### 添加包依赖 #### 函数原型 ::: tip API ```lua add_packages(packages: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | packages | 包名称字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个包名称字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 在target作用域中,添加集成包依赖,例如: ```lua target("test") add_packages("zlib", "polarssl", "pcre", "mysql") ``` 这样,在编译test目标时,如果这个包存在的,将会自动追加包里面的宏定义、头文件搜索路径、链接库目录,也会自动链接包中所有库。 用户不再需要自己单独调用[add\_links](#add_links),[add\_includedirs](#add_includedirs), [add\_ldflags](#add_ldflags)等接口,来配置依赖库链接了。 而在v2.2.2版本之后,此接口也同时支持远程依赖包管理中[add\_requires](/zh/api/description/global-interfaces#add-requires)定义的包。 ```lua add_requires("zlib", "polarssl") target("test") add_packages("zlib", "polarssl") ``` v2.2.3之后,还支持覆写内置的links,控制实际链接的库: ```lua -- 默认会有 ncurses, panel, form等links add_requires("ncurses") target("test") -- 显示指定,只使用ncurses一个链接库 add_packages("ncurses", {links = "ncurses"}) ``` 或者干脆禁用links,只使用头文件: ```lua add_requires("lua") target("test") add_packages("lua", {links = {}}) ``` ## add\_languages ### 添加语言标准 #### 函数原型 ::: tip API ```lua add_languages(languages: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | languages | 语言标准字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个语言标准字符串 | 与[set\_languages](#set_languages)类似,唯一区别是这个接口不会覆盖掉之前的设置,而是追加设置。 ## add\_vectorexts ### 添加向量扩展指令 #### 函数原型 ::: tip API ```lua add_vectorexts(vectorexts: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | vectorexts | 向量扩展指令字符串或数组,如 "mmx", "neon", "avx" | | ... | 可变参数,可传入多个向量扩展指令字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | 添加扩展指令优化选项,目前支持以下几种扩展指令集: ```lua add_vectorexts("mmx") add_vectorexts("neon") add_vectorexts("avx", "avx2", "avx512") add_vectorexts("sse", "sse2", "sse3", "ssse3", "sse4.2") ``` :::tip 注意 如果当前设置的指令集编译器不支持,xmake会自动忽略掉,所以不需要用户手动去判断维护,只需要将你需要的指令集全部设置上就行了。 ::: 2.8.2 新增了一个 `all` 配置项,可以用于尽可能的开启所有扩展指令优化。 ```lua add_vectorexts("all") ``` ## add\_frameworks ### 添加链接框架 #### 函数原型 ::: tip API ```lua add_frameworks(frameworks: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | frameworks | 框架名称字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个框架名称字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 目前主要用于`ios`和`macosx`平台的`objc`和`swift`程序,例如: ```lua target("test") add_frameworks("Foundation", "CoreFoundation") ``` 当然也可以使用[add\_mxflags](#add_mxflags)和[add\_ldflags](#add_ldflags)来设置,不过比较繁琐,不建议这样设置。 ```lua target("test") add_mxflags("-framework Foundation", "-framework CoreFoundation") add_ldflags("-framework Foundation", "-framework CoreFoundation") ``` 如果不是这两个平台,这些设置将会被忽略。 ## add\_frameworkdirs ### 添加链接框架搜索目录 #### 函数原型 ::: tip API ```lua add_frameworkdirs(frameworkdirs: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | frameworkdirs | 框架搜索目录字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个框架搜索目录字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | #### 用法说明 对于一些第三方framework,那么仅仅通过[add\_frameworks](#add_frameworks)是没法找到的,还需要通过这个接口来添加搜索目录。 ```lua target("test") add_frameworks("MyFramework") add_frameworkdirs("/tmp/frameworkdir", "/tmp/frameworkdir2") ``` ## set\_toolset ### 设置工具集 #### 函数原型 ::: tip API ```lua set_toolset(toolname: , tool: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | toolname | 工具名称字符串,如 "cc", "cxx", "ld", "ar" | | tool | 工具路径字符串,如 "/usr/bin/gcc" | #### 用法说明 针对特定target单独设置切换某个编译器,链接器,不过我们更推荐使用[set\_toolchains](#set_toolchains)对某个target进行整体工具链的切换。 与set\_toolchains相比,此接口只切换工具链某个特定的编译器或者链接器。 :::tip 注意 2.3.4以上版本才支持此接口,2.3.4之前的set\_toolchain/set\_tool接口会逐步弃用,采用此新接口,用法相同。 ::: 对于`add_files("*.c")`添加的源码文件,默认都是会调用系统最匹配的编译工具去编译,或者通过`xmake f --cc=clang`命令手动去修改,不过这些都是全局影响所有target目标的。 如果有些特殊需求,需要对当前工程下某个特定的target目标单独指定不同的编译器、链接器或者特定版本的编译器,这个时候此接口就可以排上用途了,例如: ```lua target("test1") add_files("*.c") target("test2") add_files("*.c") set_toolset("cc", "$(projectdir)/tools/bin/clang-5.0") ``` 上述描述仅对test2目标的编译器进行特殊设置,使用特定的clang-5.0编译器来编译test2,而test1还是使用默认设置。 :::tip 注意 每次设置都会覆盖当前target目标下之前的那次设置,不同target之间不会被覆盖,互相独立,如果在根域设置,会影响所有子target。 ::: 前一个参数是key,用于指定工具类型,目前支持的有(编译器、链接器、归档器): | 工具类型 | 描述 | | ------------ | ------------------------------------ | | cc | c编译器 | | cxx | c++编译器 | | mm | objc编译器 | | mxx | objc++编译器 | | gc | go编译器 | | as | 汇编器 | | sc | swift编译器 | | rc | rust编译器 | | dc | dlang编译器 | | fc | fortran编译器 | | sc | swift编译器 | | rust | rust编译器 | | strip | strip程序 | | ld | c/c++/asm/objc等通用可执行程序链接器 | | sh | c/c++/asm/objc等通用动态库链接器 | | ar | c/c++/asm/objc等通用静态库归档器 | | dcld | dlang可执行链接器, rcld/gcld等类似 | | dcsh | dlang动态库链接器, rcsh/gcsh等类似 | 对于一些编译器文件名不规则,导致xmake无法正常识别处理为已知的编译器名的情况下,我们也可以加一个工具名提示,例如: ```lua set_toolset("cc", "gcc@$(projectdir)/tools/bin/mipscc.exe") ``` 上述描述设置mipscc.exe作为c编译器,并且提示xmake作为gcc的传参处理方式进行编译。 ## set\_toolchains ### 设置工具链 #### 函数原型 ::: tip API ```lua set_toolchains(toolchains: , ..., { vs = , plat = , arch = , clang = , gcc = , vs_sdkver = , vs_toolset = , ... = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | toolchains | 工具链名称字符串或数组,如 "gcc", "clang", "msvc", "ndk\[gcc]", "mingw\[clang]@llvm-mingw", "msvc\[vs=2025]" | | vs | Visual Studio 版本,如 "2022", "2025" | | plat | 平台名称,如 "android", "ios" | | arch | 架构名称,如 "arm64", "x64" | | clang | 在 MinGW 工具链中使用 Clang 编译器,布尔值 | | gcc | 在 Android NDK 工具链中使用 GCC 编译器,布尔值 | | vs\_sdkver | Visual Studio SDK 版本 | | vs\_toolset | Visual Studio 工具集版本 | | ... | 可变参数,可传入多个工具链名称字符串 | #### 用法说明 这对某个特定的target单独切换设置不同的工具链,和set\_toolset不同的是,此接口是对完整工具链的整体切换,比如cc/ld/sh等一系列工具集。 这也是推荐做法,因为像gcc/clang等大部分编译工具链,编译器和链接器都是配套使用的,要切就得整体切,单独零散的切换设置会很繁琐。 ### 工具链配置语法 从 xmake 3.0.5 开始,可以使用简化的语法进行工具链配置: ```lua -- 基础工具链名称 set_toolchains("gcc", "clang", "msvc") -- 带配置选项(新语法) set_toolchains("ndk[gcc]") -- 在 Android NDK 中使用 GCC set_toolchains("mingw[clang]@llvm-mingw") -- 在 MinGW 中使用 Clang,来自 llvm-mingw 包 set_toolchains("msvc[vs=2025]") -- 使用 Visual Studio 2025 -- 传统配置语法 set_toolchains("ndk", {gcc = true}) -- 在 Android NDK 中使用 GCC set_toolchains("mingw", {clang = true}) -- 在 MinGW 中使用 Clang(需要 add_requires("llvm-mingw")) set_toolchains("msvc", {vs = "2025"}) set_toolchains("msvc", {vs = "2022", vs_sdkver = "10.0.19041.0"}) set_toolchains("ndk", {plat = "android", arch = "arm64", gcc = true}) ``` ### 包依赖 使用 `mingw[clang]@llvm-mingw` 语法时,需要添加包依赖: ```lua add_requires("llvm-mingw") set_toolchains("mingw[clang]@llvm-mingw") ``` 或使用传统语法: ```lua add_requires("llvm-mingw") set_toolchains("mingw", {clang = true}) ``` ### 命令行使用 也可以通过命令行指定工具链配置: ```bash # 切换到 Android NDK 中的 GCC xmake f -p android --ndk=~/Downloads/android-ndk-r14b/ --toolchain=ndk[gcc] -c # 切换到 MinGW 中的 Clang(包依赖自动处理) xmake f --toolchain=mingw[clang]@llvm-mingw -c # 切换到 Visual Studio 2025 xmake f --toolchain=msvc[vs=2025] -c ``` 比如我们切换test目标到clang+yasm两个工具链: ```lua target("test") set_kind("binary") add_files("src/*.c") set_toolchains("clang", "yasm") ``` 只需要指定工具链名字即可,具体xmake支持哪些工具链,可以通过下面的命令查看: ```sh $ xmake show -l toolchains xcode Xcode IDE vs VisualStudio IDE yasm The Yasm Modular Assembler clang A C language family frontend for LLVM go Go Programming Language Compiler dlang D Programming Language Compiler sdcc Small Device C Compiler cuda CUDA Toolkit ndk Android NDK rust Rust Programming Language Compiler llvm A collection of modular and reusable compiler and toolchain technologies cross Common cross compilation toolchain nasm NASM Assembler gcc GNU Compiler Collection mingw Minimalist GNU for Windows gnu-rm GNU Arm Embedded Toolchain envs Environment variables toolchain fasm Flat Assembler ``` 当然,我们也可以通过命令行全局切换到其他工具链: ```sh $ xmake f --toolchain=clang $ xmake ``` 另外,我们也可以在xmake.lua中自定义toolchain,然后通过`set_toolchains`指定进去,例如: ```lua toolchain("myclang") set_kind("standalone") set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") -- ... ``` 关于这块的详情介绍,可以到[自定义工具链](/zh/api/description/custom-toolchain)章节查看 更多详情见:[#780](https://github.com/xmake-io/xmake/issues/780) 2.3.5版本开始,新增对toolchains平台和架构的单独设置和切换,比如: ```lua target("test") set_toolchains("xcode", {plat = os.host(), arch = os.arch()}) ``` 如果当前是在交叉编译模式,那么这个test还是会强制切到xcode的本地编译工具链和对应的pc平台上去,这对于想要同时支持部分target使用主机工具链,部分target使用交叉编译工具链时候,非常有用。 但是,这还不是特别方便,尤其是跨平台编译时候,不同平台的pc工具链都是不同的,有msvc, xcode, clang等,还需要判断平台来指定。 因此,我们可以直接使用[set\_plat](#set_plat)和[set\_arch](#set_arch)接口,直接设置特定target到主机平台,就可以内部自动选择host工具链了,例如: ```lua target("test") set_plat(os.host()) set_arch(os.arch()) ``` 这块的应用场景和example可以看下:https://github.com/xmake-io/xmake-repo/blob/dev/packages/l/luajit/port/xmake.lua luajit里面就需要同时编译host平台的minilua/buildvm来生成jit相关代码,然后开始针对性编译luajit自身到不同的交叉工具链。 关于这块详情,可以参考:https://github.com/xmake-io/xmake/pull/857 v2.5.1 对 set\_toolchains 做了进一步的改进,更好地对特定 target 支持独立工具链切换,比如不同 target 支持切换到不同的 vs 版本,例如: ```lua target("test") set_toolchains("msvc", {vs = "2015"}) ``` 默认 xmake 会使用全局 vs 工具链,比如当前检测到 vs2019,但是用户同时还安装了 vs2015,那么可以通过上面的配置将 test 目标切换到 vs2015 来编译。 甚至还可以配合 `set_arch` 来指定特定的架构到 x86,而不是默认的 x64。 ```lua target("test") set_arch("x86") set_toolchains("msvc", {vs = "2015"}) ``` 上面的效果跟 `set_toolchains("msvc", {vs = "2015", arch = "x86"})` 类似,不过 `set_arch` 是针对 target 粒度的,而 `set_toolchains` 里面的 arch 设置仅仅针对特定工具链粒度。 通常,我们更推荐使用 `set_arch` 来对整个target实现架构切换。 v3.0.4 以上版本,对于 mingw 工具链,我们还可以通过 `msystem` 参数来指定使用特定的 MSYS2 环境,例如: ```lua target("ucrt64") set_arch("x86_64") set_kind("binary") add_files("src/*.c") set_toolchains("mingw", {msystem = "ucrt64"}) ``` 这样可以让 xmake 使用 MSYS2 的 ucrt64 环境中的 mingw 工具链来编译,支持的 msystem 值包括:`mingw32`、`mingw64`、`ucrt64`、`clang64` 等。 ## set\_plat ### 设置指定目标的编译平台 #### 函数原型 ::: tip API ```lua set_plat(plat: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | plat | 平台名称字符串,如 "linux", "macosx", "windows", "android" | #### 用法说明 通常配合[set\_arch](#set_arch)使用,将指定target的编译平台切换到指定平台,xmake会自动根据切换的平台,选择合适的工具链。 一般用于需要同时编译host平台目标、交叉编译目标的场景,更多详情见:[set\_toolchains](#set_toolchains) 例如: ```sh $ xmake f -p android --ndk=/xxx ``` 即使正在使用android ndk编译android平台目标,但是其依赖的host目标,还是会切换到主机平台,使用xcode, msvc等host工具链来编译。 ```lua target("host") set_kind("binary") set_plat(os.host()) set_arch(os.arch()) add_files("src/host/*.c") target("test") set_kind("binary") add_deps("host") add_files("src/test/*.c") ``` ## set\_arch ### 设置指定目标的编译架构 #### 函数原型 ::: tip API ```lua set_arch(arch: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | arch | 架构名称字符串,如 "x86", "x64", "arm64", "armv7" | #### 用法说明 详情见:[set\_plat](#set_plat) ## set\_values ### 设置一些扩展配置值 #### 函数原型 ::: tip API ```lua set_values(name: , values: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置名称字符串,如 "markdown\_flags" | | values | 配置值,可以是任意类型 | | ... | 可变参数,可传入多个配置值 | #### 用法说明 给target设置一些扩展的配置值,这些配置没有像`set_ldflags`这种内置的api可用,通过第一个参数传入一个配置名,来扩展配置。 一般用于传入配置参数给自定义rule中的脚本使用,例如: ```lua rule("markdown") on_build_file(function (target, sourcefile, opt) -- compile .markdown with flags local flags = 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规则去处理。 在规则脚本中可以通过`values("markdown.flags")`获取到target中设置的扩展flags值。 :::tip 注意 具体扩展配置名,根据不同的rule,会有所不同,目前有哪些,可以参考相关规则的描述:[内建规则](/zh/api/description/builtin-rules)。 ::: 下面是一些 xmake 目前支持的一些内置的扩展配置项列表。 | 扩展配置名 | 配置描述 | | --- | --- | | fortran.moduledir | 设置 fortran 模块的输出目录 | | ndk.arm\_mode | 设置 ndk 的 arm 编译模式 (arm/thumb) | | objc.build.arc | 设置启用或禁用 objc 的 arc | | objc++.build.arc | 设置启用或禁用 objc++ 的 arc | | xcode.bundle\_identifier | 设置 xcode 工具链的 Bundle Identifier | | xcode.mobile\_provision | 设置 xcode 工具链的证书信息 | | xcode.codesign\_identity | 设置 xcode 工具链的代码签名标识 | | wasm.preloadfiles | 设置 wasm 打包的预加载文件 (preload file) | | wdk.env.winver | 设置 wdk 的 win 支持版本 | | wdk.umdf.sdkver | 设置 wdk 的 umdf sdk 版本 | | wdk.kmdf.sdkver | 设置 wdk 的 kmdf sdk 版本 | | wdk.sign.mode | 设置 wdk 的代码签名模式 | | wdk.sign.store | 设置 wdk 的代码签名 store | | wdk.sign.certfile | 设置 wdk 的代码签名证书文件 | | wdk.sign.thumbprint | 设置 wdk 的代码签名指纹 | | wdk.sign.digest\_algorithm | 设置 wdk 的代码签名摘要算法 | ## add\_values ### 添加一些扩展配置值 #### 函数原型 ::: tip API ```lua add_values(name: , values: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置名称字符串,如 "markdown\_flags" | | values | 配置值,可以是任意类型 | | ... | 可变参数,可传入多个配置值 | #### 用法说明 用法跟[set\_values](#set_values)类似,区别就是这个接口是追加设置,而不会每次覆盖设置。 ## set\_rundir ### 设置运行目录 #### 函数原型 ::: tip API ```lua set_rundir(rundir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | rundir | 运行目录路径字符串 | #### 用法说明 此接口用于设置默认运行target程序的当前运行目录,如果不设置,默认情况下,target是在可执行文件所在目录加载运行。 如果用户想要修改加载目录,一种是通过`on_run()`的方式自定义运行逻辑,里面去做切换,但仅仅为了切个目录就这么做,太过繁琐。 因此可以通过这个接口快速的对默认执行的目录环境做设置切换。 ```lua target("test") set_kind("binary") add_files("src/*.c") set_rundir("$(projectdir)/xxx") ``` ## set\_runargs ### 设置运行参数列表 #### 函数原型 ::: tip API ```lua set_runargs(runargs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | runargs | 运行参数字符串或数组,如 "-x", "--arg1=val" | | ... | 可变参数,可传入多个运行参数字符串 | #### 用法说明 2.6.9 新增接口,可用于设置 `xmake run` 的默认运行参数,通过它,我们可以避免每次命令行输入运行参数,`xmake run -x --arg1=val` ```lua set_runargs("-x", "--arg1=val") ``` ## add\_runenvs ### 添加运行环境变量 #### 函数原型 ::: tip API ```lua add_runenvs(name: , values: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 环境变量名称字符串,如 "PATH", "LD\_LIBRARY\_PATH" | | values | 环境变量值字符串或数组,支持多个值 | | ... | 可变参数,可传入多个环境变量值 | #### 用法说明 此接口用于添加设置默认运行target程序的环境变量,跟[set\_runenv](#set_runenv)不同的是,此接口是对已有系统env中的值进行追加,并不会覆盖。 所以,对于PATH这种,通过此接口追加值是非常方便的,而且此接口支持多值设置,所以通常就是用来设置带有path sep的多值env。。 ```lua target("test") set_kind("binary") add_files("src/*.c") add_runenvs("PATH", "/tmp/bin", "xxx/bin") add_runenvs("LD_LIBRARY_PATH", "/tmp/lib", "xxx/lib") ``` ## set\_runenv ### 设置运行环境变量 #### 函数原型 ::: tip API ```lua set_runenv(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 环境变量名称字符串,如 "PATH", "LD\_LIBRARY\_PATH" | | value | 环境变量值字符串 | 此接口跟[add\_runenvs](#add_runenvs)不同的是,`set_runenv`是对某个环境变量的覆盖设置,会覆盖原有系统环境的env值,并且此接口是单数设置,不能传递多参。 所以,如果要覆盖设置PATH这中多路径的env,需要自己去拼接: ```lua target("test") set_kind("binary") add_files("src/*.c") set_runenv("PATH", path.joinenv("/tmp/bin", "xxx/bin")) set_runenv("NAME", "value") ``` ## set\_installdir ### 设置安装目录 #### 函数原型 ::: tip API ```lua set_installdir(installdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | installdir | 安装目录路径字符串 | #### 用法说明 2.2.5版本新增接口,用于针对每个target设置不同的默认安装目录,一般用于`xmake install/uninstall`命令。 默认情况下执行`xmake install`会安装到系统`/usr/local`目录,我们除了可以通过`xmake install -o /usr/local`指定其他安装目录外, 还可以在xmake.lua中针对target设置不同的安装目录来替代默认目录。 除了上述两种方式,我们也可以通过`INSTALLDIR`和`DESTDIR`环境变量设置默认的安装目录。 ## set\_prefixdir ### 设置安装前置子目录 #### 函数原型 ::: tip API ```lua set_prefixdir(prefixdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | prefixdir | 安装前置子目录路径字符串 | #### 用法说明 尽管通过 `set_installdir` 和 `xmake install -o [installdir]` 设置了安装根目录,但是如果我们还想进一步调整 bin, lib 和 include 的子路径。 那么,我们可以使用这个接口,默认情况下,安装目录会按照这个结构: ```sh installdir - bin - lib - include ``` 如果我们配置: ```lua set_prefixdir("prefixdir") ``` 就是增加一个总的子目录: ```sh installdir - prefixdir - bin - lib - include ``` 我们还可以单独配置 bin, lib 和 include 子目录,例如: ```lua set_prefixdir("prefixdir", {bindir = "mybin", libdir = "mylib", includedir = "myinc"}) ``` ```sh installdir - prefixdir - mybin - mylib - myinc ``` 如果,我们不配置 prefixdir,仅仅修改 bin 子目录,可以将 prefixdir 配置成 `/`。 ```lua set_prefixdir("/", {bindir = "mybin", libdir = "mylib", includedir = "myinc"}) ``` ```sh installdir - mybin - mylib - myinc ``` ## add\_installfiles ### 添加安装文件 #### 函数原型 ::: tip API ```lua add_installfiles(installfiles: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | installfiles | 安装文件路径字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个安装文件路径字符串 | | prefixdir | 安装前缀目录,可选 | | rootdir | 根目录,可选 | | filename | 文件名,可选 | #### 用法说明 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](#set_installdir)接口,来配合使用。 关于此接口的详细说明,见:https://github.com/xmake-io/xmake/issues/318 ## add\_headerfiles ### 添加安装头文件 #### 函数原型 ::: tip API ```lua add_headerfiles(headerfiles: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | headerfiles | 头文件路径字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个头文件路径字符串 | | prefixdir | 安装前缀目录,可选 | | rootdir | 根目录,可选 | | filename | 文件名,可选 | #### 用法说明 2.2.5版本新增接口,用于针对每个target设置对应需要安装的头文件,一般用于`xmake install/uninstall`命令。 此接口使用方式跟[add\_installfiles](#add_installfiles)接口几乎完全一样,都可以用来添加安装文件,不过此接口仅用于安装头文件。 因此,使用上比`add_installfiles`简化了不少,默认不设置prefixdir,也会自动将头文件安装到对应的`include`子目录中。 并且此接口对于`xmake project -k vs201x`等插件生成的IDE文件,也会添加对应的头文件进去。 我注:默认安装不会保留目录结构,会完全展开,当然们也可以通过`()`去提取源文件中的子目录结构来安装,例如: ```lua target("test") add_headerfiles("src/(tbox/*.h)", {prefixdir = "include"}) ``` v2.7.1 之后,我们可以通过 `{install = false}` 参数,禁用默认的头文件安装行为,仅仅对设置的头文件用于 project generator 的文件列表展示和编辑,例如 vs project。 ```lua add_headerfiles("src/foo.h") add_headerfiles("src/test.h", {install = false}) ``` 上面两个头文件,在 vs 工程中都会展示出来,但是仅仅 foo.h 会被发布安装到系统。 ## set\_configdir ### 设置模板配置文件的输出目录 #### 函数原型 ::: tip API ```lua set_configdir(configdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | configdir | 模板配置文件输出目录路径字符串 | #### 用法说明 2.2.5版本新增接口,主要用于[add\_configfiles](#add_configfiles)接口设置的模板配置文件的输出目录。 ## set\_configvar ### 设置模板配置变量 #### 函数原型 ::: tip API ```lua set_configvar(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置变量名称字符串,如 "HAS\_FOO" | | value | 配置变量值,可以是任意类型 | #### 用法说明 2.2.5版本新增接口,用于在编译前,添加一些需要预处理的模板配置变量,一般用于[add\_configfiles](#add_configfiles)接口。 ```lua target("test") set_kind("binary") add_files("main.c") set_configvar("HAS_FOO", 1) set_configvar("HAS_BAR", "bar") set_configvar("HAS_ZOO", "zoo", {quote = false}) add_configfiles("config.h.in") ``` config.h.in ```c ${define HAS_FOO} ${define HAS_BAR} ${define HAS_ZOO} ``` 生成的 config.h 内容如下: ```c define HAS_FOO 1 define HAS_BAR "bar" define HAS_ZOO zoo ``` set\_configvar 可以设置 number,string 和 boolean 类型值,如果是 string 值,默认生成的宏定义带有引号,如果要去掉引号,可以设置 `{quote = false}`。 相关 issues 见:[#1694](https://github.com/xmake-io/xmake/issues/1694) 对于,宏定义里面有路径,需要转义处理路径分隔符的,我们也可以配置开启路径字符转义。 ```lua set_configvar("TEST", "C:\\hello", {escape = true}) ``` 它会自动转义成 `#define TEST "C:\\hello"` ,如果没开启转义,则会变成:`#define TEST "C:\hello"` 相关 issues 见:[#1872](https://github.com/xmake-io/xmake/issues/1872) ## add\_configfiles ### 添加模板配置文件 #### 函数原型 ::: tip API ```lua add_configfiles(configfiles: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | configfiles | 配置文件路径字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个配置文件路径字符串 | | prefixdir | 安装前缀目录,可选 | | rootdir | 根目录,可选 | | filename | 文件名,可选 | #### 用法说明 2.2.5版本新增接口,用于在编译前,添加一些需要预处理的配置文件。 先来一个简单的例子: ```lua target("test") set_kind("binary") add_files("src/*.c") set_configdir("$(builddir)/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](#add_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", {onlycopy = true}) ``` 通过[set\_configvar](#set_configvar)接口设置模板变量,裹着通过`{variables = {xxx = ""}}`中设置的变量进行替换处理。 预处理后的文件`config.h`内容为: ``` define VAR1 "1" define VAR2 "2" define HELLO "xmake" ``` 而`{onlycopy = 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 ``` v2.5.3 后新增 git 相关内置变量: ```c define GIT_COMMIT "${GIT_COMMIT}" define GIT_COMMIT_LONG "${GIT_COMMIT_LONG}" define GIT_COMMIT_DATE "${GIT_COMMIT_DATE}" define GIT_BRANCH "${GIT_BRANCH}" define GIT_TAG "${GIT_TAG}" define GIT_TAG_LONG "${GIT_TAG_LONG}" define GIT_CUSTOM "${GIT_TAG}-${GIT_COMMIT}" ``` ```c define GIT_COMMIT "8c42b2c2" define GIT_COMMIT_LONG "8c42b2c251793861eb85ffdf7e7c2307b129c7ae" define GIT_COMMIT_DATE "20210121225744" define GIT_BRANCH "dev" define GIT_TAG "v1.6.6" define GIT_TAG_LONG "v1.6.6-0-g8c42b2c2" define GIT_CUSTOM "v1.6.6-8c42b2c2" ``` #### 宏定义 我们还可以对`#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 #### 定义导出宏 v2.9.8 新增的特性,可以生成动态库的导出宏定义,通常用于 windows 下 dll 库的符号导出和导入。 在 config.h.in 中定义: ```c ${define_export MYLIB} ``` 就会生成 ```c ifdef MYLIB_STATIC define MYLIB_EXPORT else if defined(_WIN32) define MYLIB_EXPORT __declspec(dllexport) elif defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) define MYLIB_EXPORT __attribute__((visibility("default"))) else define MYLIB_EXPORT endif endif ``` 我们在定义动态库导出符号时,可以通过这个宏来控制导入导出。 ```c MYLIB_EXPORT void foo(); ``` 它跟 CMake 的 [GenerateExportHeader](https://cmake.org/cmake/help/latest/module/GenerateExportHeader.html) 的功能类似。 不过,它不会额外生成一个独立的导出头文件,而是直接在 config.h 中去生成它。 更多详情见:[#6088](https://github.com/xmake-io/xmake/issues/6088) #### 自定义预处理器 如果 xmake 内置的生成规则不满足需求,也可以自定义处理器去重写生成规则,例如重写 `${define_export XXX}`: ```lua target("test") set_kind("binary") add_files("main.c") add_configfiles("config.h.in", { preprocessor = function (preprocessor_name, name, value, opt) if preprocessor_name == "define_export" then value = ([[#ifdef %s_STATIC define %s_EXPORT else if defined(_WIN32) define %s_EXPORT __declspec(dllexport) elif defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) define %s_EXPORT __attribute__((visibility("default"))) else define %s_EXPORT endif endif ]]):format(name, name, name, name, name) return value end end}) ``` 我们也可以重写对 `${define XXX}` 和 `${default XXX}` 的生成,甚至自定义扩展其他预处理配置。 例如: ```lua target("test") set_kind("binary") add_files("main.c") set_configvar("FOO", "foo") add_configfiles("config.h.in", { preprocessor = function (preprocessor_name, name, value, opt) local argv = opt.argv if preprocessor_name == "define_custom" then return string.format("#define CUSTOM_%s %s", name, value) end end}) ``` 然后我们在 config.h.in 中配置: ```c ${define_custom FOO arg1 arg2} ``` 其中,`define_custom` 是自定义的预处理器名,FOO 是变量名,可以从 `set_configvar` 中获取变量值。 而 arg1, arg2 是可选的预处理参数列表,根据实际的需求来判断是否需要使用,如果想要使用参数,可以通过 `opt.argv` 来获取,它是一个参数列表 table。 在运行 `xmake config` 后,就会在 config.h 中自动生成如下配置: ```c define CUSTOM_FOO foo ``` ## set\_policy ### 设置构建行为策略 #### 函数原型 ::: tip API ```lua set_policy(policy: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | policy | 策略名称字符串,如 "check.auto\_ignore\_flags", "build.warning" | | value | 策略值,true表示启用,false表示禁用 | #### 用法说明 xmake有很多的默认行为,比如:自动检测和映射flags、跨target并行构建等,虽然提供了一定的智能化处理,但重口难调,不一定满足所有的用户的使用习惯和需求。 因此,从v2.3.4开始,xmake提供默认构建策略的修改设置,开放给用户一定程度上的可配置性。 使用方式如下: ```lua set_policy("check.auto_ignore_flags", false) ``` 只需要在项目根域设置这个配置,就可以禁用flags的自动检测和忽略机制,另外`set_policy`也可以针对某个特定的target局部生效。 ```lua target("test") set_policy("check.auto_ignore_flags", false) ``` ## set\_runtimes ### 设置编译目标依赖的运行时库 #### 函数原型 ::: tip API ```lua set_runtimes(runtimes: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | runtimes | 运行时库字符串或数组,如 "MT", "MD", "MTd", "MDd" | | ... | 可变参数,可传入多个运行时库字符串 | 这是 v2.5.1 开始新增的接口,用于抽象化设置编译目标依赖的运行时库,目前仅仅支持对 msvc 运行时库的抽象,但后续也许会扩展对其他编译器运行时库的映射。 目前支持的一些配置值说明如下: | 值 | 描述 | | ------ | ----------------------------------------- | | MT | msvc 运行时库:多线程静态库 | | MTd | msvc 运行时库:多线程静态库(调试) | | MD | msvc 运行时库:多线程动态库 | | MDd | msvc 运行时库:多线程动态库(调试) | | c++\_static | clang 的 c++ 运行时库,静态库 | | c++\_shared | clang 的 c++ 运行时库,动态库 | | stdc++\_static | gcc 的 c++ 运行时库,静态库 | | stdc++\_shared | gcc 的 c++ 运行时库,动态库 | | gnustl\_static | android 的 c++ 运行时库,静态库,高版本 NDK 已废弃 | | gnustl\_shared | android 的 c++ 运行时库,静态库,高版本 NDK 已废弃 | | stlport\_static | android 的 c++ 运行时库,静态库,高版本 NDK 已废弃 | | stlport\_static | android 的 c++ 运行时库,静态库,高版本 NDK 已废弃 | 关于 vs 运行时,可以参考:[msvc 运行时说明](https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-160) 而这个接口传入 MT/MTd 参数配置,xmake 会自动配置上 `/MT /nodefaultlib:msvcrt.lib` 参数。 我们可以针对不同的 target 设置不同的运行时。 另外,如果我们将 `set_runtimes` 设置在全局根域,那么所有的 `add_requires("xx")` 包定义也会全局同步切换到对应的 vs runtime 配置 ```lua set_runtimes("MD") add_requires("libcurl", "fmt") target("test") set_kind("binary") add_files("src/*.c") ``` 当然,我们也可以通过 `add_requires("xx", {configs = {vs_runtime = "MD"}})` 对特定包修改 vs 运行时库。 我们也可以通过 `xmake f --vs_runtime='MD'` 通过参数配置来全局切换它。 与此 api 相关的 issue:[#1071](https://github.com/xmake-io/xmake/issues/1071#issuecomment-750817681) ## set\_group ### 设置目标分组 #### 函数原型 ::: tip API ```lua set_group(group: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | group | 分组名称字符串,如 "test", "libs", "tools" | #### 用于工程文件分组展示 此接口可用于 vs/vsxmake 工程生成,对 vs 工程内部子工程目录树按指定结构分组展示,不过后续也可能对其他模块增加分组支持。 比如对于下面的分组配置: ```lua add_rules("mode.debug", "mode.release") target("test1") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test2") set_kind("binary") add_files("src/*.cpp") set_group("group1") target("test3") set_kind("binary") add_files("src/*.cpp") set_group("group1/group2") target("test4") set_kind("binary") add_files("src/*.cpp") set_group("group3/group4") target("test5") set_kind("binary") add_files("src/*.cpp") target("test6") set_kind("binary") add_files("src/*.cpp") ``` 生成的 vs 工程目录结构效果如下: ![](/assets/img/manual/set_group.png) 其中 `set_group("group1/group2")` 可以将 target 设置到二级分组中去。 更多详情见:[#1026](https://github.com/xmake-io/xmake/issues/1026) #### 编译指定一批目标程序 我们可以使用 `set_group()` 将给定的目标标记为 `test/benchmark/...` 并使用 `set_default(false)` 禁用来默认构建它。 然后,通过 `xmake -g xxx` 命令就能指定构建一批目标程序了。 比如,我们可以使用此功能来构建所有测试。 ```lua target("test1") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") target("test2") set_kind("binary") set_default(false) set_group("test") add_files("src/*.cpp") ``` ```sh $ xmake -g test $ xmake --group=test ``` #### 运行指定一批目标程序 我们也可以通过设置分组,来指定运行所有带有 `test` 分组的测试程序。 ```sh $ xmake run -g test $ xmake run --group=test ``` 另外,我们还可以支持分组的模式匹配: ``` $ xmake build -g test_* $ xmake run -g test/foo_* $ xmake build -g bench* $ xmake run -g bench* ``` 更多信息见:[#1913](https://github.com/xmake-io/xmake/issues/1913) ## add\_filegroups ### 添加源文件分组 #### 函数原型 ::: tip API ```lua add_filegroups(name: , files: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 分组名称字符串,如 "source", "header", "test" | | files | 源文件路径字符串或数组,支持通配符匹配模式 | | ... | 可变参数,可传入多个源文件路径字符串 | 这个接口目前主要用于对 vs/vsxmake/cmakelists generator 生成的工程文件进行源文件分组展示。 如果不设置分组展示,Xmake 也会默认按照树状模式展示,但是有些极端情况下,目录层级显示不是很好,例如: ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") ``` ![](/assets/img/manual/filegroup1.png) 目前主要支持两种展示模式: * plain: 平坦模式 * tree: 树形展示,这也是默认模式 另外,它也支持对 `add_headerfiles` 添加的文件进行分组。 #### 设置分组并指定根目录 ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") add_filegroups("group1/group2", {rootdir = "../../../../"}) ``` ![](/assets/img/manual/filegroup2.png) #### 设置分组并指定文件匹配模式 ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") add_filegroups("group1/group2", {rootdir = "../../../../", files = {"src/**.cpp"}}) ``` #### 作为平坦模式展示 这种模式下,所有源文件忽略嵌套的目录层级,在分组下同一层级展示。 ```lua target("test") set_kind("binary") add_files("../../../../src/**.cpp") add_filegroups("group1/group2", {rootdir = "../../../../", mode = "plain"}) ``` ![](/assets/img/manual/filegroup3.png) ## set\_exceptions ### 启用或者禁用异常 #### 函数原型 ::: tip API ```lua set_exceptions(exceptions: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | exceptions | 异常模式字符串或数组,如 "cxx", "objc", "no-cxx" | | ... | 可变参数,可传入多个异常模式字符串 | 我们可以通过这个配置,配置启用和禁用 C++/Objc 的异常。 通常,如果我们通过 add\_cxxflags 接口去配置它们,需要根据不同的平台,编译器分别处理它们,非常繁琐。 例如: ```lua on_config(function (target) if (has_tool("cxx", "cl")) then add("cxflags", "/EHsc", {force = true}) add("defines", "_HAS_EXCEPTIONS=1", {force = true}) elseif(has_tool("cxx", "clang") or has_tool("cxx", "clang-cl")) then add("cxflags", "-fexceptions", {force = true}) add("cxflags", "-fcxx-exceptions", {force = true}) end end) ``` 而通过这个接口,我们就可以抽象化成编译器无关的方式去配置它们。 开启 C++ 异常: ```lua set_exceptions("cxx") ``` 禁用 C++ 异常: ```lua set_exceptions("no-cxx") ``` 我们也可以同时配置开启 objc 异常。 ```lua set_exceptions("cxx", "objc") ``` 或者禁用它们。 ```lua set_exceptions("no-cxx", "no-objc") ``` Xmake 会在内部自动根据不同的编译器,去适配对应的 flags。 ## set\_encodings ### 设置编码 #### 函数原型 ::: tip API ```lua set_encodings(encodings: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | encodings | 编码字符串或数组,如 "utf-8", "gb2312" | | ... | 可变参数,可传入多个编码字符串 | 这是 2.8.2 版本新增的接口,我们可以用这个接口设置源文件、目标执行文件的编码。 目前支持的编码:utf-8, gb2312 (msvc) 默认情况下,我们仅仅指定编码,是会同时对源文件,目标文件生效。 ```lua -- for all source/target encodings set_encodings("utf-8") -- msvc: /utf-8 ``` 它等价于: ```lua set_encodings("source:utf-8", "utf-8") ``` 并且,目前仅仅支持设置成 utf-8 编码,将来会不断扩展。 如果,我们仅仅想单独设置源文件编码,或者目标文件编码,也是可以的。 #### 设置源文件编码 通常指的是编译的代码源文件的编码,我们可以这么设置。 ```lua -- gcc/clang: -finput-charset=UTF-8, msvc: -source-charset=utf-8 set_encodings("source:utf-8") ``` #### 设置目标文件编码 它通常指的是目标可执行文件的运行输出编码。 ```lua -- gcc/clang: -fexec-charset=UTF-8, msvc: -target-charset=utf-8 set_encodings("utf-8") ``` ## add\_forceincludes ### 强制添加 includes #### 函数原型 ::: tip API ```lua add_forceincludes(includes: , ..., { public|interface|private = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 头文件路径字符串或数组,如 "config.h" | | ... | 可变参数,可传入多个头文件路径字符串 | | public|interface|private | 可见性设置,详见[可见性设置](#visibility) | 这是 2.8.2 新增的接口,用于在配置文件中直接强制添加 `includes` 头文件。 ```lua add_forceincludes("config.h") ``` 它的效果类似于 `#include `,但是不需要在源码中显式添加它了。 另外,它的搜索路径也是需要通过 `add_includedirs` 来控制,而不是直接配置文件路径。 ```lua add_forceincludes("config.h") add_includedirs("src") ``` 默认 add\_forceincludes 匹配 c/c++/objc。如果仅仅只想匹配 c++ 可以这么配置: ```lua add_forceincludes("config.h", {sourcekinds = "cxx"}) ``` 如果想同时匹配多个源文件类型,也是可以的: ```lua add_forceincludes("config.h", {sourcekinds = {"cxx", "mxx"}}) ``` ## add\_extrafiles ### 添加额外的文件 #### 函数原型 ::: tip API ```lua add_extrafiles(extrafiles: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | extrafiles | 额外文件路径字符串或数组,如 "assets/other.txt" | | ... | 可变参数,可传入多个额外文件路径字符串 | #### 用法说明 这个接口也是 2.8.2 新加的,主要用于 vs/vsxmake project generator 生成的工程中,添加额外的文件到工程列表中去,这样,用户也可以快速点击编辑它们,尽管它们不是代码文件。 将来,我们也可能用此接口做更多其他的事情。 ```lua add_extrafiles("assets/other.txt") ``` ## add\_tests ### 添加测试用例 #### 函数原型 ::: tip API ```lua add_tests(tests: , ..., { runargs = , runenvs =
, timeout = , ... = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tests | 测试用例名称字符串或数组,如 "test1", "test2" | | ... | 可变参数,可传入多个测试用例名称字符串 | | runargs | 测试运行参数字符串或数组,可选 | | runenvs | 测试运行环境变量表,可选 | | timeout | 测试超时时间(秒),可选 | | ... | 其他测试配置参数,可选 | #### 用法说明 2.8.5 版本开始,我们增加了内置的测试命令:`xmake test`,我们只需要在需要测试的 target 上通过 add\_tests 配置一些测试用例,就可以自动执行测试。 即使当前 target 被设置成了 `set_default(false)`,在执行测试的时候,xmake 也还是会先自动编译它们,然后自动运行所有的测试。 我们可以先看个整体的例子,大概知道下它是怎么样子的。 ```lua add_rules("mode.debug", "mode.release") for _, file in ipairs(os.files("src/test_*.cpp")) do local name = path.basename(file) target(name) set_kind("binary") set_default(false) add_files("src/" .. name .. ".cpp") add_tests("default") add_tests("args", {runargs = {"foo", "bar"}}) add_tests("pass_output", {trim_output = true, runargs = "foo", pass_outputs = "hello foo"}) add_tests("fail_output", {fail_outputs = {"hello2 .*", "hello xmake"}}) end ``` 这个例子,自动扫描源码目录下的 `test_*.cpp` 源文件,然后每个文件自动创建一个测试目标,它被设置成了 `set_default(false)`,也就是正常情况下,默认不会编译它们。 但是,如果执行 `xmake test` 进行测试,它们就会被自动编译,然后测试运行,运行效果如下: ```sh ruki-2:test ruki$ xmake test running tests ... [ 2%]: test_1/args .................................... passed 7.000s [ 5%]: test_1/default .................................... passed 5.000s [ 8%]: test_1/fail_output .................................... passed 5.000s [ 11%]: test_1/pass_output .................................... passed 6.000s [ 13%]: test_2/args .................................... passed 7.000s [ 16%]: test_2/default .................................... passed 6.000s [ 19%]: test_2/fail_output .................................... passed 6.000s [ 22%]: test_2/pass_output .................................... passed 6.000s [ 25%]: test_3/args .................................... passed 7.000s [ 27%]: test_3/default .................................... passed 7.000s [ 30%]: test_3/fail_output .................................... passed 6.000s [ 33%]: test_3/pass_output .................................... passed 6.000s [ 36%]: test_4/args .................................... passed 6.000s [ 38%]: test_4/default .................................... passed 6.000s [ 41%]: test_4/fail_output .................................... passed 5.000s [ 44%]: test_4/pass_output .................................... passed 6.000s [ 47%]: test_5/args .................................... passed 5.000s [ 50%]: test_5/default .................................... passed 6.000s [ 52%]: test_5/fail_output .................................... failed 6.000s [ 55%]: test_5/pass_output .................................... failed 5.000s [ 58%]: test_6/args .................................... passed 7.000s [ 61%]: test_6/default .................................... passed 6.000s [ 63%]: test_6/fail_output .................................... passed 6.000s [ 66%]: test_6/pass_output .................................... passed 6.000s [ 69%]: test_7/args .................................... failed 6.000s [ 72%]: test_7/default .................................... failed 7.000s [ 75%]: test_7/fail_output .................................... failed 6.000s [ 77%]: test_7/pass_output .................................... failed 5.000s [ 80%]: test_8/args .................................... passed 7.000s [ 83%]: test_8/default .................................... passed 6.000s [ 86%]: test_8/fail_output .................................... passed 6.000s [ 88%]: test_8/pass_output .................................... failed 5.000s [ 91%]: test_9/args .................................... passed 6.000s [ 94%]: test_9/default .................................... passed 6.000s [ 97%]: test_9/fail_output .................................... passed 6.000s [100%]: test_9/pass_output .................................... passed 6.000s 80% tests passed, 7 tests failed out of 36, spent 0.242s ``` ![](/assets/img/manual/xmake-test1.png) 我们也可以执行 `xmake test -vD` 查看详细的测试失败的错误信息: ![](/assets/img/manual/xmake-test2.png) #### 运行指定测试目标 我们也可以指定运行指定 target 的某个测试: ```sh $ xmake test targetname/testname ``` 或者按模式匹配的方式,运行一个 target 的所有测试,或者一批测试: ```sh $ xmake test targetname/* $ xmake test targetname/foo* ``` 也可以运行所有 target 的同名测试: ```sh $ xmake test */testname ``` #### 并行化运行测试 其实,默认就是并行化运行的,但是我们可以通过 `-jN` 调整运行的并行度。 ```sh $ xmake test -jN ``` #### 分组运行测试 ```sh $ xmake test -g "foo" $ xmake test -g "foo*" ``` #### 添加测试到目标(无参数) 如果没有配置任何参数,仅仅配置了测试名到 `add_tests`,那么仅仅测试这个目标程序的是否会运行失败,根据退出代码来判断是否通过测试。 ``` target("test") add_tests("testname") ``` #### 配置运行参数 我们也可以通过 `{runargs = {"arg1", "arg2"}}` 的方式,给 `add_tests` 配置指定测试需要运行的参数。 另外,一个 target 可以同时配置多个测试用例,每个测试用例可独立运行,互不冲突。 ```lua target("test") add_tests("testname", {runargs = "arg1"}) add_tests("testname", {runargs = {"arg1", "arg2"}}) ``` 如果我们没有配置 runargs 到 `add_tests`,那么我们也会尝试从被绑定的 target 中,获取 `set_runargs` 设置的运行参数。 ```lua target("test") add_tests("testname") set_runargs("arg1", "arg2") ``` #### 配置运行目录 我们也可以通过 rundir 设置测试运行的当前工作目录,例如: ```lua target("test") add_tests("testname", {rundir = os.projectdir()}) ``` 如果我们没有配置 rundir 到 `add_tests`,那么我们也会尝试从被绑定的 target 中,获取 `set_rundir` 设置的运行目录。 ```lua target("test") add_tests("testname") set_rundir("$(projectdir)") ``` #### 配置运行环境 我们也可以通过 runenvs 设置一些运行时候的环境变量,例如: ```lua target("test") add_tests("testname", {runenvs = {LD_LIBRARY_PATH = "/lib"}}) ``` 如果我们没有配置 runenvs 到 `add_tests`,那么我们也会尝试从被绑定的 target 中,获取 `add_runenvs` 设置的运行环境。 ```lua target("test") add_tests("testname") add_runenvs("LD_LIBRARY_PATH", "/lib") ``` #### 匹配输出结果 默认情况下,`xmake test` 会根据测试运行的退出代码是否为 0,来判断是否测试通过。 当然,我们也可以通过配置测试运行的输出结果是否满足我们的指定的匹配模式,来判断是否测试通过。 主要通过这两个参数控制: | 参数 | 说明 | | --- | --- | | pass\_outputs | 如果输出匹配,则测试通过 | | fail\_outputs | 如果输出匹配,则测试失败 | 传入 `pass_outputs` 和 `fail_outputs` 的是一个 lua 匹配模式的列表,但模式稍微做了一些简化,比如对 `*` 的处理。 如果要匹配成功,则测试通过,可以这么配置: ```lua target("test") add_tests("testname1", {pass_outputs = "hello"}) add_tests("testname2", {pass_outputs = "hello *"}) add_tests("testname3", {pass_outputs = {"hello", "hello *"}}) ``` 如果要匹配成功,则测试失败,可以这么配置: ```lua target("test") add_tests("testname1", {fail_outputs = "hello"}) add_tests("testname2", {fail_outputs = "hello *"}) add_tests("testname3", {fail_outputs = {"hello", "hello *"}}) ``` 我们也可以同时配置它们: ```lua target("test") add_tests("testname", {pass_outputs = "foo", fail_outputs = "hello"}) ``` 由于一些测试输出的结果,尾部会有一些换行什么的空白字符,干扰匹配模式,我们可以再配置 `trim_output = true`,先截断空白字符后,再做匹配。 ```lua target("test") add_tests("testname", {trim_output = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` 我们还可以配置 `{plain = true}` 是禁用 lua 模式匹配,仅仅做最基础的平坦文本匹配。 ```lua target("test") add_tests("testname", {plain = true, pass_outputs = "foo", fail_outputs = "hello"}) ``` #### 配置测试组 我们也可以通过 `group = "foo"` 来配置一个测试组,进行分组测试: ```lua target("test") add_tests("testname1", {group = "foo"}) add_tests("testname2", {group = "foo"}) add_tests("testname3", {group = "bar"}) add_tests("testname4", {group = "bae"}) ``` 其中 testname1/testname2 是一个组 foo,另外两个是在另外一个组。 然后,我们就可以使用 `xmake test -g groupname` 来进行分组测试了。 ```sh $ xmake test -g "foo" $ xmake test -g "foo*" ``` :::tip 注意 运行分组,也是支持模式匹配的。 ::: 另外,如果没有设置 `group` 参数给 `add_tests`,我们也可以默认获取绑定到 target 的组名。 ```lua target("test") add_tests("testname") set_group("foo") ``` #### 自定义测试脚本 我们还新增了 `before_test`, `on_test` 和 `after_test` 配置脚本,用户可以在 rule 和 target 域,自定义配置它们实现定制化的测试执行。 ```lua target("test") on_test(function (target, opt) print(opt.name, opt.runenvs, opt.runargs, opt.pass_outputs) -- do test -- ... -- passed return true -- failied return false, errors end) ``` 其中,opt 里面可以获取到所有传入 `add_tests` 的参数,我们在 on\_test 里面自定义测试逻辑,然后返回 true 就是测试通过,返回 false 就是测试失败,然后继续返回测试失败的错误信息。 #### 自动化构建 由于测试目标在正常开发构建阶段,通常是不需要被构建的,因此我们会设置 `set_default(false)`。 ```lua target("test") add_tests("testname") set_default(false) ``` 但是运行 `xmake test` 进行测试时候,这些测试对应的 target 还是会被自动构建,确保能够被运行。 ```sh $ xmake test [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test running tests ... [100%]: test/testname .................................... passed 6.000s 100% tests passed, 0 tests failed out of 1, spent 0.006s ``` #### 标记测试为预期失败 你可以使用 `should_fail` 选项将测试标记为预期失败。这在验证以下情况时非常有用: * 崩溃处理 * 断言或契约违规 * 正在开发或已知存在问题的功能 如果一个设置了 `should_fail = true` 的测试在执行时失败了,它将被视为成功。但如果它意外通过,则会在测试报告中高亮显示为 **unexpected pass**,并将该测试标记为失败。 测试摘要也会将预期失败和意外通过的测试进行分组展示。 ```lua target("test") set_kind("binary") add_files("tests/test_foo.cpp") add_tests("crash_div_by_zero", { runargs = { "--div-zero" }, should_fail = true }) add_tests("crash_div_by_zero_fails", { runargs = { "--div-zero" } }) add_tests("normal_behavior", { runargs = { "--safe" } }) add_tests("normal_behavior_unexpected_pass", { runargs = { "--safe" }, should_fail = true }) ``` 示例输出: ``` ... report of tests: [ 25%]: test/crash_div_by_zero ............. expected failure 0.004s [ 50%]: test/crash_div_by_zero_fails........ failed 0.004s [ 75%]: test/normal_behavior ............... passed 0.015s [100%]: test/normal_behavior_unexpected_pass unexpected pass 0.015s Detailed summary: Failed tests: - test/crash_div_by_zero_fails Unexpected passes: - test/normal_behavior_unexpected_pass Expected failures: - test/crash_div_by_zero 50% tests passed, 1 test(s) failed, 1 unexpected pass(es), 1 expected failure(s) out of 4, spent 0.038s ``` #### 首次测试失败就终止 默认情况下,`xmake test` 会等到所有测试都运行完,不管里面有多少是没通过的。 而有时候,我们想在第一个测试没通过,就直接中断测试,那么我们可以通过下面的配置启用: ```lua set_policy("test.stop_on_first_failure", true) ``` #### 测试失败返回0 默认情况下,只要有一个测试没通过,等到 `xmake test` 运行完成,它都会返回非0退出代码,这对于一些 CI 环境非常有用,可以中断 CI 的其他脚本继续运行。 然后触发信号告诉 CI,我们需要生成测试报告和告警了。 然后,如果我们想要压制这种行为,可以强制将 `xmake test` 的退出代码总是设置成 0。 ```lua set_policy("test.return_zero_on_failure", true) ``` #### 仅仅测试编译 有时候,我们仅仅想要测试代码是否通过编译,或者没有通过编译,不需要运行它们,那么可以通过配置 `build_should_pass` 和 `build_should_fail` 来实现。 ```lua target("test_10") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_fail", {build_should_fail = true}) target("test_11") set_kind("binary") set_default(false) add_files("src/compile.cpp") add_tests("compile_pass", {build_should_pass = true}) ``` 这通常用于一些测试代码中带有 `static_assert` 的场景,例如: ```c++ template bool foo(T val) { if constexpr (std::is_same_v) { printf("int!\n"); } else if constexpr (std::is_same_v) { printf("float!\n"); } else { static_assert(false, "unsupported type"); } } int main(int, char**) { foo("BAD"); return 0; } ``` #### 配置额外的代码编译 我们还可以在配置测试用例的时候,对每个测试配置额外需要编译的代码,以及一些宏定义,实现内联测试。 xmake 会为每个测试单独编译一个独立的可执行程序去运行它,但这并不会影响到 target 在生产环境的编译结果。 ```lua target("test_13") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_1", {files = "tests/stub_1.cpp", defines = "STUB_1"}) target("test_14") set_kind("binary") set_default(false) add_files("src/test_2.cpp") add_tests("stub_2", {files = "tests/stub_2.cpp", defines = "STUB_2"}) target("test_15") set_kind("binary") set_default(false) add_files("src/test_1.cpp") add_tests("stub_n", {files = "tests/stub_n*.cpp", defines = "STUB_N"}) ``` 以 doctest 为例,我们可以在不修改任何 main.cpp 的情况下,外置单元测试: ```lua add_rules("mode.debug", "mode.release") add_requires("doctest") target("doctest") set_kind("binary") add_files("src/*.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { files = testfile, remove_files = "src/main.cpp", languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` 定义 DOCTEST\_CONFIG\_IMPLEMENT\_WITH\_MAIN 会引入额外的 main 入口函数,因此我们需要配置 remove\_files 去移除已有的 main.cpp 文件。 运行效果如下: ```sh ruki-2:doctest ruki$ xmake test running tests ... [ 50%]: doctest/test_1 .................................... failed 0.009s [100%]: doctest/test_2 .................................... passed 0.009s 50% tests passed, 1 tests failed out of 2, spent 0.019s ruki-2:doctest ruki$ xmake test -v running tests ... [ 50%]: doctest/test_1 .................................... failed 0.026s [doctest] doctest version is "2.4.11" [doctest] run with "--help" for options =============================================================================== tests/test_1.cpp:7: TEST CASE: testing the factorial function tests/test_1.cpp:8: ERROR: CHECK( factorial(1) == 10 ) is NOT correct! values: CHECK( 1 == 10 ) =============================================================================== [doctest] test cases: 1 | 0 passed | 1 failed | 0 skipped [doctest] assertions: 4 | 3 passed | 1 failed | [doctest] Status: FAILURE! run failed, exit code: 1 [100%]: doctest/test_2 .................................... passed 0.010s 50% tests passed, 1 tests failed out of 2, spent 0.038s ``` #### 测试动态库 通常,`add_tests` 仅用于对可执行程序进行运行测试,运行动态库需要有一个额外的 main 主入口,因此我们需要额外配置一个可执行程序去加载它,例如: ```lua target("doctest_shared") set_kind("shared") add_files("src/foo.cpp") for _, testfile in ipairs(os.files("tests/*.cpp")) do add_tests(path.basename(testfile), { kind = "binary", files = testfile, languages = "c++11", packages = "doctest", defines = "DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN"}) end ``` 通过 `kind = "binary"` 可以将每个单元测试改为 binary 可执行程序,并通过 DOCTEST\_CONFIG\_IMPLEMENT\_WITH\_MAIN 引入 main 入口函数。 这样就能实现动态库目标中外置可运行的单元测试。 #### 配置运行超时 如果一些测试程序长时间运行不退出,就会卡住,我们可以通过配置超时时间,强制退出,并返回失败。 ```lua target("test_timeout") set_kind("binary") set_default(false) add_files("src/run_timeout.cpp") add_tests("run_timeout", {run_timeout = 1000}) ``` ```sh $ xmake test [100%]: test_timeout/run_timeout .................................... failed 1.006s run failed, exit code: -1, exit error: wait process timeout ``` --- --- url: /zh/guide/best-practices/faq.md --- # 常见问题 {#faq} ## 怎样获取更多参数选项信息?{#get-command-line-arguments} 获取主菜单的帮助信息,里面有所有 action 和 plugin 的列表描述。 ```sh $ xmake [-h|--help] ``` 获取配置菜单的帮助信息,里面有所有配置选项的描述信息,以及支持的平台、架构列表。 ```sh $ xmake f [-h|--help] ``` 获取 action 和 plugin 命令菜单的帮助信息,里面有所有内置命令和插件任务的参数使用信息。 ```sh $ xmake [action|plugin] [-h|--help] ``` 例如,获取 `run` 命令的参数信息: ```sh $ xmake run --help ``` ## 怎样实现静默构建,不输出任何信息?{#suppress-all-output-info} ```sh $ xmake [-q|--quiet] ``` ## 如果 xmake 运行失败了怎么办?{#what-do-do-if-xmake-fails} 可以先尝试清除一下配置,重新构建: ```sh $ xmake f -c $ xmake ``` 如果还是失败了,请加上 `-v` 或者 `--verbose` 选项重新执行 xmake 后,获取更加详细的输出信息 例如: ```sh $ xmake [-v|--verbose] ``` 并且可以加上 `-D` 选项获取出错时 xmake 的调试栈信息和其他更详细的诊断信息, 然后你可以将这些信息提交到 [issues](https://github.com/xmake-io/xmake/issues)。 ```sh $ xmake -v -D ``` ## 怎样看实时编译警告信息? {#see-verbose-compiling-warnings} 为了避免刷屏,在构建时默认是不实时输出警告信息的,如果想要查看,可以加上 `-w` 选项启用编译警告输出。 ```sh $ xmake [-w|--warning] ``` ## 为什么 xmake.lua 会被执行多遍?{#executed-multiple-times} xmake.lua 里面分为描述域和脚本域,在描述域中会对各种配置域进行分阶段多次解析,有可能会执行多遍,因此不要在描述域中写复杂的脚本。 如果要写各种复杂脚本,请在脚本域内进行配置,`target/on_load` 的脚本域中同样可以灵活配置各种 target 相关设置,并且提供更强大的 Lua 脚本模块支持。 更多细节见:[描述语法说明](/zh/guide/project-configuration/syntax-description)。 ## 如何调试 Xmake 源码? {#debugging-xmake-sourcecode} ### 下载源码 由于 Xmake 使用了 git submodules 维护子模块,因此我们可以通过以下几种方式拉取完整源码。 #### 使用 git 拉取 ```sh $ git clone --recursive https://github.com/xmake-io/xmake.git ``` 或者 ```sh $ git clone https://github.com/xmake-io/xmake.git $ git submodule update --init ``` #### 从 Github Releases 下载源码包 由于 github 本身的 downloads 附件下载不支持归档 submodules,因此 Xmake 每次发版都会完整打包一份额外的 tar 包源码上传到 Releases。 因此,不要下载错误的链接地址: * 不完整源码:https://github.com/xmake-io/xmake/archive/refs/tags/v2.7.2.tar.gz * 完整源码包:https://github.com/xmake-io/xmake/releases/download/v2.7.2/xmake-v2.7.2.tar.gz ```sh wget https://github.com/xmake-io/xmake/releases/download/v2.7.2/xmake-v2.7.2.tar.gz tar -xvf xmake-v2.7.2.tar.gz -C xmake cd xmake ``` ::: tip NOTE Xmake 的 tar 源码包没有顶层 xmake 根目录,因此解压时最好带上 `-C xmake` 指定输出目录。 ::: ### 编译源码 #### 在 Windows 上编译 如果是在 Windows 编译 Xmake 源码,需要借助现有的 Xmake 预构建版本进行自举编译。 因此我们需要先参考 [Windows 安装 Xmake](/zh/guide/quick-start#windows) 文档,安装 Xmake。 然后进入 Xmake 源码目录进行编译。 ```sh cd xmake cd core xmake ``` ::: tip 注意 我们需要进入 Xmake 的 core 子目录执行 xmake 命令。 ::: #### 在 Linux/macOS/FreeBSD 上编译 其他类 unix 平台环境编译 Xmake,我们只需要在源码根目录执行 make 就行了。 ```sh $ cd xmake $ ./configure $ make ``` ### 加载调试 如果编译完成,我们就可以加载刚刚编译好的 Xmake 二进制 core 程序,然后运行本地的 Lua 脚本了。 #### 在 Windows 上加载本地调试环境 进入 `xmake/scripts` 目录,双击 srcenv.bat 脚本,它会自动加载本地的 Xmake 程序和脚本,打开一个 cmd 终端。 我们在这个终端下,就可以开启调试了。 我们也可以运行 ```sh $ xmake l os.programdir ``` 来验证我们是否真的加载了本地的 Lua 脚本环境。 #### 在其他平台加载本地调试环境 在 Linux/macOS/FreeBSD 上会更加简单,只需要运行: ```sh $ cd xmake $ source scripts/srcenv.profile ``` 就能进入本地源码调试环境。 ### 调试 core 二进制 通常调试 Xmake 的 Lua 脚本,只需要直接修改当前源码目录的 Lua 脚本就行了,实时生效,我们并不需要重复编译 core 二进制。 但是如果是 Xmake 的 C 端 core 程序有问题,需要调试或者加模块,那么就需要重复编译了。 编译完成后,也是实时生效的,我们可以在 C 代码里通过: ```c tb_trace_i("hello %s", "xmake"); ``` 来格式化打印各种输出。 如果是 tbox 等 Xmake 依赖的各种 submodules 子模块有问题,需要调试。 我们也可以直接进入子模块源码,修改后重新编译执行。 但是,如果需要贡献修复补丁,我们需要提交 pr 给子模块的仓库才行,补丁合并后,作者会在特定时间同步到到 Xmake 源码仓库。 ### 断点调试 2.8.3 版本,我们新增了 Lua 断点调试支持,配合 [VSCode-EmmyLua](https://github.com/EmmyLua/VSCode-EmmyLua) 插件,我们可以很方便地在 VSCode 中断点调试 Xmake 自身源码。 首先,我们需要在 VSCode 的插件市场安装 VSCode-EmmyLua 插件,然后执行下面的命令更新 xmake-repo 仓库保持最新。 ```sh xrepo update-repo ``` ::: tip 注意 Xmake 也需要保持最新版本。 ::: 然后,在自己的工程目录下执行以下命令: ```sh $ xrepo env -b emmylua_debugger -- xmake build ``` 其中 `xrepo env -b emmylua_debugger` 用于绑定 EmmyLua 调试器插件环境,而 `--` 后面的参数,就是我们实际需要被调试的 xmake 命令。 通常我们仅调试 `xmake build` 构建,如果想要调试其他命令,可以自己调整,比如想要调试 `xmake install -o /tmp` 安装命令,那么可以改成: ```sh $ xrepo env -b emmylua_debugger -- xmake install -o /tmp ``` 执行完上面的命令后,它不会立即退出,会一直处于等待调试状态,有可能没有任何输出。 这个时候,我们不要急着退出它,继续打开 VSCode,并在 VSCode 中打开 Xmake 的 Lua 脚本源码目录。 也就是这个目录:[Xmake Lua Scripts](https://github.com/xmake-io/xmake/tree/master/xmake),我们可以下载到本地,也可以直接打开 Xmake 安装目录中的 lua 脚本目录。 然后切换到 VSCode 的调试 Tab 页,点击 `RunDebug` -> `Emmylua New Debug` 就能连接到我们的 `xmake build` 命令调试端,开启调试。 如下图所示,默认的起始断点会自动中断到 `debugger:_start_emmylua_debugger` 内部,我们可以点击单步跳出当前函数,就能进入 main 入口。 ![](/assets/img/manual/xmake-debug.png) 然后设置自己的断点,点击继续运行,就能中断到自己想要调试的代码位置。 我们也可以在项目工程的配置脚本中设置断点,也可以实现快速调试自己的配置脚本,而不仅仅是 Xmake 自身源码。 ![](/assets/img/manual/xmake-debug2.png) ### 远程调试 2.8.3 版本现在也能支持远程调试,其实这个功能主要是给作者用的,因为作者本人的开发电脑是 mac,但是有时候还是需要能够在 windows 上调试 xmake 源码脚本。 但是在虚拟机中调试,太卡,体验不好,并且作者本人的电脑磁盘空间不够,因此我通常会远程连到单独的 windows 主机上去调试 xmake 源码。 我们先在 windows 机器上开启远程编译服务: ```sh $ xmake service ``` 然后本机打开需要构建的工程目录,执行远程连接,然后执行 `xmake service --sync --xmakesrc=` 去同步本地源码: ```sh $ xmake service --connect $ xmake service --sync --xmakesrc=~/projects/personal/xmake/xmake/ $ xmake build $ xmake run ``` 这样,我们就能本地修改 xmake 脚本源码,然后同步到远程 windows 机器上,然后远程执行 xmake 构建命令,获取对应的调试输出,以及分析构建行为。 我们也能够通过 `xmake service --pull=` 命令,回拉远程的文件到本地,进行分析。 注:详细的远程编译特性说明,见 [远程编译文档](/zh/guide/extras/remote-compilation)。 ![](/assets/img/manual/xmake-remote.png) ## 如何调试仓库包? {#debugging-repository-packages} 调试的方式有很多种,这里我主要介绍作者最常使用的调试方式,那就是直接拉取 xmake-repo 仓库来调试。 ```sh $ git clone https://github.com/xmake-io/xmake-repo.git $ xmake l scripts/test.lua -vD --shallow zlib ``` 使用上面 test.lua 脚本命令来调试包,我们可以重复安装测试指定的包,`--shallow` 告诉 Xmake 每次测试不去重复完整安装它的所有依赖包,仅仅测试按照当前包。 我们也可以测试指定的平台,架构,编译模式,vs\_runtime 和动态库,静态库等等。 ```sh $ xmake l scripts/test.lua -vD --shallow -p mingw --mingw=/xxx/sdk zlib $ xmake l scripts/test.lua -vD --shallow -p iphoneos -a arm64 zlib $ xmake l scripts/test.lua -vD --shallow -k shared --vs_runtime=MD zlib $ xmake l scripts/test.lua -vD --shallow -m debug zlib ``` ### 调试本地包源码 有时候,由于包的源码和构建脚本有问题,我们需要修改一些代码才能继续测试安装,如果通过 add\_patches/io.replace 的方式在 on\_install 里面去修改调试,非常繁琐。 因此,我们可以通过指定 `-d package_sourcedir` 方式,直接让测试脚本进入我们预先下载好的包源码目录,测试编译安装,我们每次的代码修改不会被重置。 ```sh $ xmake l scripts/test.lua -vD --shallow -d /tmp/zlib-1.2.11 zlib ``` 等修改调试通过后,我们再根据改动,通过 `git diff > fix.patch` 生成补丁文件,通过 `add_patches` 配置应用补丁包,来修复包的安装。 ### 远程调试包源码 我们也可以远程调试包,先开启远程服务: ```sh $ xmake service ``` 然后传入 `--remote` 参数,即可实现远程包编译测试。 ```sh $ xmake l scripts/test.lua -vD --shallow --remote /tmp/zlib-1.2.11 zlib ``` ## 下载包提示证书校验失败怎么办? ```sh curl: (60) SSL certificate problem: unable to get local issuer certificate More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not establish a secure connection to it. To learn more about this situation and how to fix it, please visit the web page mentioned above. ``` 如果你在使用 Xmake 安装依赖包时候,遇到上面的证书验证问题,你可以尝试更新 curl 证书去修复它,或者直接全局配置禁用证书验证来绕过它。 ```sh $ xmake g --insecure-ssl=y ``` 当然,禁用证书验证会带来一定的安全性风险,不过好在 xmake-repo 仓库中的包,有严格的 sha256 校验, 即使下载被劫持,最终也会 xmake 的 sha256 校验检测到,作为无效下载。 --- --- url: /zh/guide/quick-start.md --- # 快速上手 {quick-start} ## 安装 {#installation} ::: tip 注意 Xmake 不推荐 root 下安装使用,因为这很不安全,如果用户非要 root 下装,装完后,如果提示xmake运行不了,请根据提示传递`--root`参数,或者设置`XMAKE_ROOT=y`环境变量强行启用下,前提是:用户需要随时注意root下误操作系统文件文件的风险。 ::: ::: code-group ```sh [curl] curl -fsSL https://xmake.io/shget.text | bash ``` ```sh [wget] wget https://xmake.io/shget.text -O - | bash ``` ```powershell [powershell] irm https://xmake.io/psget.text | iex ``` ::: 如果要安装指定版本和分支,后面可以追加版本号和分支参数 ```sh curl -fsSL https://xmake.io/shget.text | bash -s dev curl -fsSL https://xmake.io/shget.text | bash -s v2.7.7 & ([ScriptBlock]::Create((irm https://xmake.io/psget.text))) -version 2.7.7 ``` ::: tip 注意 如果ps脚本执行提示失败,可以尝试在管理员模式下执行 ::: ### Windows 1. 从 [Releases](https://github.com/xmake-io/xmake/releases) 上下载windows安装包 2. 运行安装程序 xmake-\[version].\[win32|win64].exe ::: tip 注意 Releases 下面 xmake-\[version].\[win32|win64].zip 的包是不带安装程序的,可直接解压使用,绿色无依赖,不过需要自己添加PATH环境变量。 ::: 此外,Releases下面带有 xmake-tinyc 开头的exe安装包,内部集成了 tinyc 编译器环境,自带 libc 和 winapi 头文件,安装这个包,可以实现在没有 msvc 环境下,也能正常编译 c 程序。 这对于临时想写一些 c 测试或者算法代码,又不想安装 msvc 的用户非常有用,不过安装包会稍微大2-3M,不过也还好。 ::: code-group ```sh [scoop] scoop install xmake ``` ```sh [winget] winget install xmake ``` ::: ### Msys/Mingw 现在msys/pacman官方仓库已经收录xmake软件包,可直接通过pacman安装。 ::: code-group ```sh [mingw64] pacman -Sy mingw-w64-x86_64-xmake ``` ```sh [mingw32] pacman -Sy mingw-w64-i686-xmake ``` ::: ### MacOS ```sh brew install xmake ``` ### Linux 发行版 ::: code-group ```sh [Arch Linux] sudo pacman -S xmake ``` ```sh [Alpine] sudo apk add xmake ``` ```sh [ubuntu] sudo apt install xmake ``` ```sh [debian] sudo apt install xmake ``` ```sh [fedora] sudo dnf install xmake ``` ::: ### Ubuntu PPA ```sh sudo add-apt-repository ppa:xmake-io/xmake sudo apt update sudo apt install xmake ``` ### Gentoo 1. 参考[这里](https://wiki.gentoo.org/wiki/Project:GURU/Information_for_End_Users)将GURU添加到你的系统仓库 2. 安装dev-util/xmake ```sh sudo emerge -a --autounmask dev-util/xmake ``` ### 其他 Linux 先从 [Releases](https://github.com/xmake-io/xmake/releases) 上下载xmake-x.x.x.gz.run自安装包 然后运行这个自安装包。 ```sh sudo chmod 777 ./xmake-x.x.x.gz.run ./xmake-x.x.x.gz.run ``` ### FreeBSD 由于 BSD 上,已有的 xmake 包名已被占用,只能使用 xmake-io 作为包名来安装。 ```sh pkg install xmake-io ``` ### Termux (Android) ```sh pkg install xmake ``` ### Bundle 包 如果不想安装,我们也提供了另外一种 Bundle 打包格式,它无需用户安装,单一可执行文件,下载即可运行使用,方便分发。 它会把所有 Lua 脚本内置到 Xmake 可执行文件中去,不需要额外安装和配置什么环境变量。 我们可以到 [Releases](https://github.com/xmake-io/xmake/releases) 中获取它们,目前有如下一些 Bundle 包。 ``` xmake-bundle-v2.9.8.arm64.exe xmake-bundle-v2.9.8.cosmocc xmake-bundle-v2.9.8.linux.x86_64 xmake-bundle-v2.9.8.macos.arm64 xmake-bundle-v2.9.8.macos.x86_64 xmake-bundle-v2.9.8.win32.exe xmake-bundle-v2.9.8.win64.exe ``` 其中,`.cosmocc` 后缀的包,提供了跨平台运行的能力,但是目前对 Windows 上支持还比较弱,不推荐在 windows 上使用。 另外的都是针对特定平台的单一可执行文件,用户根据自己的系统按需下载使用。 ### 源码编译安装 {#build-code-and-installation} #### 下载源码 {#download-code} ```sh git clone --recursive https://github.com/xmake-io/xmake.git cd ./xmake ``` 如果觉得github的源太慢,可以通过gitee的镜像源拉取:`clone --recursive https://gitee.com/tboox/xmake.git` 也可以如下修改 `~/.gitconfig`,永久解决github clone慢的问题 ``` [url "ssh://git@github.com/"] insteadOf = https://github.com/ ``` ::: tip 注意 由于目前xmake源码通过git submodule维护依赖,所以clone的时候需要加上`--recursive`参数同时拉取所有submodules代码,请不要直接下载tar.gz源码,因为github不会自动打包submodules里面的代码。 ::: 如果git clone的时候忘记加`--recursive`,那么也可以执行`git submodule update --init`来拉取所有submodules。 #### 编译安装 {#build-installation} ::: code-group ```sh [Linux] ./configure make -j4 ./scripts/get.sh __local__ __install_only__ source ~/.xmake/profile ``` ```sh [Windows] cd ./core xmake ``` ::: ::: tip 注意 `./get.sh __local__`是安装到`~/.local/xmake`下,然后通过`source ~/.xmake/profile`方式来加载的,所以安装完,当前终端如果执行xmake失败,提示找不到,就手动执行下 `source ~/.xmake/profile`,而下次打开终端就不需要了。 如果遇到readline相关问题,请装下readline-devel或者libreadline-dev依赖,这个是可选的,仅仅`xmake lua`命令执行REPL时候才需要。 ::: ### 更新升级 {#update} 从 v2.2.3 版本开始,新增了`xmake update`命令,来快速进行自我更新和升级,默认是升级到最新版本,当然也可以指定升级或者回退到某个版本: ```sh xmake update 2.7.1 ``` 我们也可以指定更新到master/dev分支版本: ```sh xmake update master xmake update dev ``` 从指定git源更新 ```sh xmake update github:xmake-io/xmake#master xmake update gitee:tboox/xmake#dev # gitee镜像 ``` 如果xmake/core没动过,仅仅更新xmake的lua脚本改动,可以加`-s/--scriptonly`快速更新lua脚本 ```sh xmake update -s dev ``` 最后,我们如果要卸载xmake,也是支持的:`xmake update --uninstall` ## 创建工程 {#create-project} 创建一个名叫 `hello` 的 `c++` 控制台工程: ```sh $ xmake create hello ``` 执行完后,将会生成一个简单工程结构: ``` hello ├── src │   └─main.cpp └── xmake.lua ``` 其中`xmake.lua`是工程描述文件,内容非常简单,告诉 xmake 添加`src`目录下的所有`.cpp`源文件: ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("hello") set_kind("binary") add_files("src/*.cpp") ``` ## 构建工程 {#build-project} ```sh $ cd hello $ xmake ``` ## 运行程序 {#run-program} ```sh $ xmake run ``` ## 调试程序 {#debug-program} 首先你需要切换到 debug 模式去重新编译程序。 ```sh $ xmake config -m debug $ xmake ``` 然后执行下面的命令去开始调试: ```sh $ xmake run -d hello ``` Xmake 将会使用调试器去加载程序运行,目前支持:lldb, gdb, windbg, vsjitdebugger, ollydbg 等各种调试器。 ```sh [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]$ ``` 如果想要使用指定的调试器: ```sh $ xmake f --debugger=gdb $ xmake run -d hello ``` ## 下一步 {#next-steps} * 继续阅读该指南. [创建工程](/zh/guide/basic-commands/create-project) * 查看示例. [实例](/zh/examples/cpp/basic) * 查看 API 手册. [API 手册](/zh/api/description/specification) --- --- url: /zh/guide/best-practices/performance.md --- # 性能优化 {#performance} ## 并行编译 我们可以通过 `xmake -j N` 指定同时构建的任务并行度,来加速编译。 不过默认情况下,Xmake 已经开启此特性,会自动根据当前机器的 CPU Core 资源自动评估并分配需要并行的任务数。 ## 编译缓存加速 Xmake 默认开启了本地编译缓存,这在 Linux/macOS 上提速效果非常明显。 不过在 Windows 上,由于启动进程太重,并且 msvc 内置的预处理器太慢,因此目前默认对 msvc 禁用了本地缓存。 另外,除了本地缓存,Xmake 还提供远程缓存支持,这在多台机器上共享编译缓存时非常有用。 关于这个特性的详细介绍,可以看下文档:[编译缓存](/zh/guide/extras/build-cache)。 ## Unity Build 编译加速 对于 C++ 构建,我们还可以配置开启 Unity Build 编译,将多个 C++ 源码合并到一个源文件中进行编译,以减少头文件的解析时间,效果也比较明显。 详情见:[Unity Build 编译](/zh/guide/extras/unity-build)。 ## 分布式编译 对于超大型的工程,我们还可以通过增加多台编译服务器,通过分布式编译特性来加速编译。 详情见:[分布式编译](/zh/guide/extras/distributed-compilation)。 --- --- url: /zh/api/description/xpack-interfaces.md --- # 打包接口 {#xpack-interfaces} Xpack 作为插件形式提供,它的所有 API 我们需要通过 `includes("@builtin/xpack")` 方式来引入。 ```lua includes("@builtin/xpack") xpack("test") set_version("1.0") set_homepage("https://xmake.io") add_installfiles("...") ``` ## set\_version * 设置包版本 #### 函数原型 ::: tip API ```lua set_version(version: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | version | 包版本字符串 | #### 用法说明 这个接口用于设置生成的安装包的版本: ```lua xpack("test") set_version("1.0") -- ... ``` 如果我们没有设置,但是通过 `add_targets` 绑定了安装的目标程序,那么也会使用 target 中的版本配置。 ```lua target("foo") set_version("1.0") xpack("test") add_targets("foo") -- ... ``` 我们也可以使用全局工程的版本,如果没有绑定任何 targets。 ```lua set_version("1.0") xpack("xmake") -- ... ``` ## set\_homepage * 设置主页信息 #### 函数原型 ::: tip API ```lua set_homepage(homepage: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | homepage | 主页地址字符串 | #### 用法说明 ```lua xpack("xmake") set_homepage("https://xmake.io") ``` ## set\_title * 设置标题信息 #### 函数原型 ::: tip API ```lua set_title(title: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | title | 包标题字符串 | #### 用法说明 通常用于配置安装包的简单描述,相比 `set_description` 更加简短。 ```lua xpack("xmake") set_title("Xmake build utility ($(arch))") ``` ## set\_description * 设置详细描述 #### 函数原型 ::: tip API ```lua set_description(description: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | description | 包详细描述字符串 | #### 用法说明 这个接口可以设置安装包更加详细的描述信息,可以用一到两句话详细描述下包。 ```lua xpack("xmake") set_description("A cross-platform build utility based on Lua.") ``` ## set\_author * 设置作者信息 #### 函数原型 ::: tip API ```lua set_author(author: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | author | 作者信息字符串 | #### 用法说明 我们可以设置邮箱,姓名等来描述这个包的作者。 ```lua xpack("xmake") set_author("waruqi@gmail.com") ``` ## set\_maintainer * 设置维护者信息 #### 函数原型 ::: tip API ```lua set_maintainer(maintainer: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | maintainer | 维护者信息字符串 | #### 用法说明 我们可以设置邮箱,姓名等来描述这个包的维护者。 维护者跟作者有可能是同一个人,也可能不是一个人。 ```lua xpack("xmake") set_maintainer("waruqi@gmail.com") ``` ## set\_copyright * 设置包的版权信息 #### 函数原型 ::: tip API ```lua set_copyright(copyright: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | copyright | 版权信息字符串 | #### 用法说明 ```lua xpack("xmake") set_copyright("Copyright (C) 2015-present, TBOOX Open Source Group") ``` ## set\_license * 设置包的 License #### 函数原型 ::: tip API ```lua set_license(license: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | license | License名称字符串 | #### 用法说明 目前像 srpm/rpm/deb 等包会用到,用于设置 License 名。 ```lua set_license("Apache-2.0") ``` ## set\_licensefile * 设置包的 License 文件 #### 函数原型 ::: tip API ```lua set_licensefile(licensefile: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | licensefile | License文件路径字符串 | #### 用法说明 我们可以设置 LICENSE 所在的文件路径,像 NSIS 的安装包,它还会额外将 LICENSE 页面展示给安装用户。 ```lua xpack("xmake") set_licensefile("../LICENSE.md") ``` ## set\_company * 设置包所属的公司 #### 函数原型 ::: tip API ```lua set_company(company: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | company | 公司名称字符串 | #### 用法说明 我们可以用这个接口设置包所属的公司和组织名。 ```lua xpack("xmake") set_company("tboox.org") ``` ## set\_inputkind * 设置打包的输入源类型 #### 函数原型 ::: tip API ```lua set_inputkind(inputkind: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | inputkind | 输入源类型: "binary" 或 "source" | #### 用法说明 这是个可选接口,可用于标识当前打包的输入源类型 * binary: 从二进制文件作为输入源打包,通常使用 `add_installfiles` * source: 从源文件作为输入源开始打包,通常使用 `add_sourcefiles` 这一般用于自定义的打包格式,而对于内置的格式,比如: nsis, zip, srczip 等等, 其实已经能够判断获取到当前打包的输入源是从源码开始打包,还是直接从二进制源开始打包。 因此,除非必要(比如要自定义打包格式),通常我们不需要设置它。 而我们在脚本域中,也可以通过 `package:from_source()` 和 `package:from_binary()` 来判断当前的输入源。 ```lua xpack("test") set_formats("nsis", "zip", "targz", "srczip", "srctargz", "runself") add_installfiles("src/(assets/*.png)", {prefixdir = "images"}) add_sourcefiles("(src/**)") on_load(function (package) if package:from_source() then package:set("basename", "test-$(plat)-src-v$(version)") else package:set("basename", "test-$(plat)-$(arch)-v$(version)") end end) ``` 如果上面的打包配置,如果是 nsis 包,默认从二进制文件作为输入源,进行打包,会去打包 `add_installfiles` 配置的文件。 而 `srczip`, `srctargz` 和 `runself` 是从源文件开始打包,会去打包 `add_sourcefiles` 中的文件,然后再执行打包脚本。 ## set\_formats * 设置打包格式 #### 函数原型 ::: tip API ```lua set_formats(formats: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | formats | 打包格式名称字符串或数组 | | ... | 可变参数,可传递多个格式名称 | #### 用法说明 配置当前 XPack 包需要生成的打包格式,可以同时配置多个,`xmake pack` 命令会一次性全部生成。 ::: tip 注意 有些格式如果当前平台不支持生成,会自动忽略。 ::: ```lua xpack("test") set_formats("nsis", "zip", "targz", "srczip", "srctargz", "runself") ``` 我们也可以通过命令,指定生成其中部分格式,而不是一次性全部生成。 ```sh $ xmake pack -f "nsis,zip" ``` 通过逗号分隔,指定生成 NSIS 和 zip 包,暂时忽略其他格式包。 目前支持的格式有: | 格式 | 说明 | | ---- | ---- | | nsis | Windows NSIS 安装包,二进制安装 | | zip | 二进制 zip 包,不包含安装脚本 | | targz | 二进制 tar.gz 包,不包含安装脚本 | | srczip | zip 源码包 | | srctargz | tar.gz 源码包 | | runself | 自运行 shell 脚本包,源码编译安装 | | rpm | rpm 二进制安装包 | | srpm | rpm 源码安装包 | | deb | deb 二进制安装包 (待支持) | | 其他 | 可自定义格式和安装脚本 | ## set\_basename * 设置包文件名 #### 函数原型 ::: tip API ```lua set_basename(basename: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | basename | 包文件名字符串(不含扩展名) | #### 用法说明 设置生成包的文件名,但不包含后缀名。 ```lua xpack("xmake") set_basename("xmake-v$(version)") ``` 我们也可以在其中配置 `$(version)`, `$(plat)`, `$(arch)` 等变量。 另外,想要更灵活的配置,可以再 on\_load 脚本中去配置它。 ```lua xpack("xmake") on_load(function (package) package:set("basename", "xmake-v" .. package:version()) end) ``` ## set\_extension * 设置安装包的扩展名 #### 函数原型 ::: tip API ```lua set_extension(extension: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | extension | 包扩展名字符串 | #### 用法说明 通常我们并不需要修改生成包的扩展名,因为指定了 `nsis`, `zip` 等格式后,都会有一个默认的后缀名,例如:`.exe`, `.zip`。 但是,如果我们正在自定义包格式,需要生成一个自定义的包,那么我们可能需要配置它。 ```lua xpack("mypack") set_format("myformat") set_extension(".myf") on_package(function (package) local outputfile = package:outputfile() -- TODO end) ``` 例如,这里我们自定义了一个 myformat 包格式,采用 `.myf` 的自定义后缀名,然后我们就可以在 on\_package 中生成它, `package:outputfile()` 返回的包输出文件名中就会包含这个后缀名。 ## add\_targets * 关联目标程序 #### 函数原型 ::: tip API ```lua add_targets(targets: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | targets | 目标名称字符串或数组 | | ... | 可变参数,可传递多个目标名称 | #### 用法说明 我们可以通过这个接口,配置关联需要被安装的目标 target。 ```lua target("foo") set_kind("shared") add_files("src/*.cpp") add_headerfiles("include/(*.h)") xpack("test") set_formats("nsis") add_targets("foo") ``` 当生成 test 安装包的时候,被关联的 foo 目标的可执行程序,动态库等待都会被一起打包安装。 另外,target 中通过 `add_headerfiles` 和 `add_installfiles` 配置的自定义安装文件也会被打入安装包,一起被安装。 而且我们还可以在 target 和它的 rules 中通过 `on_installcmd`, `after_installcmd` 等自定义打包安装脚本,也会被一起执行。 ## add\_components * 添加安装包组件 #### 函数原型 ::: tip API ```lua add_components(components: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | components | 组件名称字符串或数组 | | ... | 可变参数,可传递多个组件名称 | #### 用法说明 我们也支持为安装包添加自定义组件,按组件模式进行选择安装。目前仅仅对 NSIS 包会有比较的支持效果。 我们可以通过 `xpack_component()` 定义一个组件域,然后使用 `add_components()` 加指定的组件跟包进行关联绑定。 而在组件中,我们可以通过 `on_installcmd()` 编写一些自定义的安装脚本,只有当这个组件被启用的情况下,才会被执行安装。 ```lua xpack("test") add_components("LongPath") xpack_component("LongPath") set_default(false) set_title("Enable Long Path") set_description("Increases the maximum path length limit, up to 32,767 characters (before 256).") on_installcmd(function (component, batchcmds) batchcmds:rawcmd("nsis", [[ ${If} $NoAdmin == "false" ; Enable long path WriteRegDWORD ${HKLM} "SYSTEM\CurrentControlSet\Control\FileSystem" "LongPathsEnabled" 1 ${EndIf}]]) end) ``` 这里,我们使用 `batchcmds:rawcmd("nsis", "...")` 添加了一个 nsis 特有的安装命令,开启长路径支持。效果如下: ![](/assets/img/manual/nsis_4.png) 只有当我们勾选 LongPath 后,才会启用,当然,我们也可以通过 `set_default()` 配置组件默认是否处于启用状态。 除了 NSIS 包,其他包尽管没有对组件有完善的支持,但是同样会执行组件里面的脚本实现打包,仅仅可能无法显示对应的组件 UI 和勾选框。 ## set\_bindir * 设置包的二进制安装目录 #### 函数原型 ::: tip API ```lua set_bindir(bindir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | bindir | 二进制安装目录字符串 | #### 用法说明 通常生成的安装包都会有一个安装根目录,而我们可以通过这个配置指定安装目录下的 bin 目录位置。 如果没有指定,默认在 `installdir/bin`。 如果配置了 ```lua xpack("xmake") set_bindir("mybin") ``` 那么会将可执行文件安装在 `installdir/mybin` 下面,如果是 NSIS 包,安装后,还会自动设置此路径到 `%PATH%`。 ## set\_libdir * 设置包的库安装目录 #### 函数原型 ::: tip API ```lua set_libdir(libdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | libdir | 库安装目录字符串 | #### 用法说明 通常生成的安装包都会有一个安装根目录,而我们可以通过这个配置指定安装目录下的 lib 目录位置。 如果没有指定,默认在 `installdir/lib`。 如果配置了 ```lua xpack("xmake") set_libdir("mylib") ``` 那么会将静态库文件安装在 `installdir/mylib` 下面。 ## set\_includedir * 设置包的头文件安装目录 #### 函数原型 ::: tip API ```lua set_includedir(includedir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includedir | 头文件安装目录字符串 | #### 用法说明 通常生成的安装包都会有一个安装根目录,而我们可以通过这个配置指定安装目录下的 include 目录位置。 如果没有指定,默认在 `installdir/include`。 如果配置了 ```lua xpack("xmake") set_includedir("myinc") ``` 那么会将头文件安装在 `installdir/myinc` 下面。 ## set\_prefixdir * 设置包的安装前缀目录 #### 函数原型 ::: tip API ```lua set_prefixdir(prefixdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | prefixdir | 安装前缀目录字符串 | #### 用法说明 如果配置了 ```lua xpack("xmake") set_prefixdir("prefix") ``` 那么会将所有安装文件,安装在 `installdir/prefix` 下面,例如: ``` installdir - prefix - include - lib - bin ``` ## set\_specfile * 设置包 spec 文件路径 #### 函数原型 ::: tip API ```lua set_specfile(specfile: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | specfile | spec文件路径字符串 | #### 用法说明 有些包格式的生成,需要先生成特定的 spec 文件,然后才能调用第三方打包工具去生成包。 比如 NSIS 包,需要先通过 xmake 根据 xpack 配置,生成 NSIS 特有的 `.nsi` 配置文件,然后 xmake 会再调用 `makensis.exe` 去根据这个 `.nsi` 文件生成 NSIS 包。 而 deb/rpm 等包都有特定的 spec 文件。 xmake 在打包的时候,默认会自动生成一个 spec 文件,但是如果我们想更加深度定制化一些特有包的配置,可以通过这个接口, 配置一个自己的 spec 文件,里面用户自己维护了一些包配置定义,然后可以在里面定义一些 `${PACKAGE_NAME}`, `${VERSION}` 包特有的内置变量,就可以实现包信息替换。 ```lua xpack("xmake") set_formats("nsis") set_specfile("makensis.nsi") ``` makensis.nsi ``` VIProductVersion "${VERSION}.0" VIFileVersion "${VERSION}.0" VIAddVersionKey /LANG=0 ProductName "${PACKAGE_NAME}" VIAddVersionKey /LANG=0 Comments "${PACKAGE_DESCRIPTION}" VIAddVersionKey /LANG=0 CompanyName "${PACKAGE_COMPANY}" VIAddVersionKey /LANG=0 LegalCopyright "${PACKAGE_COPYRIGHT}" VIAddVersionKey /LANG=0 FileDescription "${PACKAGE_NAME} Installer - v${VERSION}" VIAddVersionKey /LANG=0 OriginalFilename "${PACKAGE_FILENAME}" ``` 下面是一些内置的常用包变量: | 变量名 | 描述 | | ------ | ---- | | PACKAGE\_ARCH | 包二进制文件的架构 | | PACKAGE\_PLAT | 包二进制文件的平台 | | PACKAGE\_NAME | 包名 | | PACKAGE\_TITLE | 包的简单描述 | | PACKAGE\_DESCRIPTION | 包的详细描述 | | PACKAGE\_FILENAME | 包文件名 | | PACKAGE\_AUTHOR | 包作者 | | PACKAGE\_MAINTAINER | 包维护者 | | PACKAGE\_HOMEPAGE | 包主页地址 | | PACKAGE\_COPYRIGHT | 包的版权信息 | | PACKAGE\_COMPANY | 包所属的公司名 | | PACKAGE\_ICONFILE | 包的图标文件路劲 | | PACKAGE\_LICENSEFILE | 包的 LICENSE 文件路径 | | PACKAGE\_VERSION\_MAJOR | 包的 major 版本 | | PACKAGE\_VERSION\_MINOR | 包的 minor 版本 | | PACKAGE\_VERSION\_ALTER | 包的 alter 版本 | | PACKAGE\_VERSION\_BUILD | 包的 build 版本 | 除了内置变量,我们也可以通过 `set_specvar` 接口去配置一些自定义的模版变量。 ## set\_specvar * 设置包 spec 文件的自定义变量 #### 函数原型 ::: tip API ```lua set_specvar(name: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 变量名称字符串 | | value | 变量值字符串 | #### 用法说明 通常配合 `set_specfile` 接口一起使用,用于在自定义的 spec 模版文件里面,设置一些自定义的包变量。 ```lua xpack("xmake") set_formats("nsis") set_specfile("makensis.nsi") set_specvar("FOO", "hello") ``` makensis.nsi ``` VIAddVersionKey /LANG=0 ProductName "${FOO}" ``` 在生成包之前,xmake 会替换 `${FOO}` 成 hello,然后再调用 `makensis.exe` 命令根据这个文件生成 NSIS 安装包。 ## set\_iconfile * 设置图标文件路径 #### 函数原型 ::: tip API ```lua set_iconfile(iconfile: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | iconfile | 图标文件路径字符串 | #### 用法说明 我们可以额外配置一个 ico 的图标文件,可以用于设置 NSIS 等一些支持图标自定义的安装包的图标。 ```lua xpack("xmake") set_iconfile("xmake.ico") ``` ## add\_sourcefiles * 添加源文件 #### 函数原型 ::: tip API ```lua add_sourcefiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | files | 源文件模式字符串或数组 | | ... | 可变参数,可传递多个文件模式 | | prefixdir | 安装前缀目录 | | rootdir | 源文件根目录 | | filename | 目标文件名 | #### 用法说明 这通常用于源码包,也就是 `srczip`, `srctargz` 这种纯源码包,以及 `runself` 格式的源码安装包。 如果是自定义的包格式,我们需要配置 `set_inputkind("source")` 开启源码包。 通过这个接口,可以自定义配置那些源文件需要被打入包中,用于后期的编译安装。 它的详细用法跟 `add_installfiles` 类似,可以参考它的文档描述。 ## add\_installfiles * 添加二进制文件 #### 函数原型 ::: tip API ```lua add_installfiles(files: , ..., { prefixdir = , rootdir = , filename = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | files | 安装文件模式字符串或数组 | | ... | 可变参数,可传递多个文件模式 | | prefixdir | 安装前缀目录 | | rootdir | 源文件根目录 | | filename | 目标文件名 | #### 用法说明 这通常用于二进制包,也就是 `nsis`, `deb` 等格式的包,这些包会直接安装二进制文件。 因此,我们可以通过这个接口额外配置一些需要被安装的二进制文件,比如:可执行文件,资源文件等等。 比如我们可以指定安装各种类型的文件到安装目录: ```lua xpack("test") add_installfiles("src/*.h") add_installfiles("doc/*.md") ``` 我们也可以指定安装到特定子目录: ```lua xpack("test") add_installfiles("src/*.h", {prefixdir = "include"}) add_installfiles("doc/*.md", {prefixdir = "share/doc"}) ``` 上面的设置,我们会安装到`installdir/include/*.h`, `installdir/share/doc/*.md`。 注:默认安装不会保留目录结构,会完全展开,当然我们也可以通过`()`去提取源文件中的子目录结构来安装,例如: ```lua xpack("test") add_installfiles("src/(tbox/*.h)", {prefixdir = "include"}) add_installfiles("doc/(tbox/*.md)", {prefixdir = "share/doc"}) ``` ## add\_buildrequires * 添加包的构建依赖 #### 函数原型 ::: tip API ```lua add_buildrequires(requires: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | requires | 构建依赖名称字符串或数组 | | ... | 可变参数,可传递多个依赖名称 | #### 用法说明 添加包的构建依赖,用于指定包构建时需要的依赖项。 这通常用于一些源码包,例如 srpm。这些源码包在安装之前,需要先构建源码,而构建源码可能会需要用到一些其他的依赖包。 我们可以通过这个接口去配置它们。 ```lua xpack("test") set_formats("srpm") on_load(function (package) local format = package:format() if format == "srpm" then package:add("buildrequires", "make") package:add("buildrequires", "gcc") package:add("buildrequires", "gcc-c++") end end) on_buildcmd(function (package, batchcmds) batchcmds:runv("make") end) ``` 由于不同的安装包,它的依赖包名会有一些差异,所以我们需要在 on\_load 脚本域针对不同的包格式,去配置它们。 ## on\_load * 自定义加载脚本 #### 函数原型 ::: tip API ```lua on_load(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 加载脚本函数,参数为package | #### 用法说明 如果在描述域中配置无法满足我们的需求,还可以在 on\_load 自定义脚本域中,进一步灵活的配置包。 这个接口会在每个 XPack 包初始化加载期间就被调用,可以在里面做一些基础配置。 例如在里面动态地修改包文件名: ```lua xpack("test") on_load(function (package) package:set("basename", "test-" .. package:version()) end) ``` ## before\_package * 自定义打包之前的脚本 #### 函数原型 ::: tip API ```lua before_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包前脚本函数,参数为package | #### 用法说明 我们可以通过这个接口配置打包之前的自定义脚本。 ```lua xpack("test") before_package(function (package) -- TODO end) ``` ## on\_package * 自定义打包脚本 #### 函数原型 ::: tip API ```lua on_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包脚本函数,参数为package | #### 用法说明 自定义打包脚本,用于实现特定的打包逻辑。 我们可以通过这个接口配置打包自定义脚本,这将会重写整个内置的打包逻辑。通常用于自定义包格式。 ```lua xpack("test") set_formats("xxx") on_package(function (package) -- TODO end) ``` ## after\_package * 自定义打包之后的脚本 #### 函数原型 ::: tip API ```lua after_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包后脚本函数,参数为package | #### 用法说明 在打包完成后执行的自定义脚本,用于后处理操作。 我们可以通过这个接口配置打包之后的自定义脚本。 ```lua xpack("test") after_package(function (package) -- TODO end) ``` ## before\_installcmd * 添加安装之前的脚本 #### 函数原型 ::: tip API ```lua before_installcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装前脚本函数,参数为package和batchcmds | #### 用法说明 在安装之前执行的自定义脚本,用于准备安装环境。 它不会重写整个安装脚本,但是会在现有的安装脚本执行之前,新增一些自定义的安装脚本: ```lua xpack("test") before_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) ``` 需要注意的是,通过 `batchcmds` 添加的 cp, mkdir 等命令都不会被立即执行,而是仅仅生成一个命令列表,后面实际生成包的时候,会将这些命令,翻译成打包命令。 ## on\_buildcmd * 自定义构建脚本 #### 函数原型 ::: tip API ```lua on_buildcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 构建脚本函数,参数为package和batchcmds | #### 用法说明 自定义构建脚本,用于实现特定的构建逻辑。 对于一些源码构建包,在安装之前,我们需要先构建源码,例如 srpm 包。 因此,我们可以通过这个接口,自定义构建脚本,例如: ```lua xpack("test") set_formats("srpm") add_sourcefiles("src/*.c") add_sourcefiles("./configure") on_buildcmd(function (package, batchcmds) batchcmds:runv("./configure") batchcmds:runv("make") end) ``` 如果我们通过 add\_targets 关联了目标程序,即使我们没有配置 `on_buildcmd`,xpack 也会默认执行 `xmake build` 命令去构建它们。 ```lua xpack("test") set_formats("srpm") add_sourcefiles("src/*.c") add_sourcefiles("./xmake.lua") ``` 另外,我们也可以使用 `add_buildrequires` 去配置一些构建依赖。 ## before\_buildcmd * 自定义构建之前的脚本 #### 函数原型 ::: tip API ```lua before_buildcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 构建前脚本函数,参数为package和batchcmds | #### 用法说明 在构建之前执行的自定义脚本,用于准备构建环境。 通过这个接口,我们可以配置构建之前的脚本。 ```lua xpack("test") set_formats("srpm") before_buildcmd(function (package, batchcmds) -- TODO end) ``` ## after\_buildcmd * 自定义构建之后的脚本 #### 函数原型 ::: tip API ```lua after_buildcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 构建后脚本函数,参数为package和batchcmds | #### 用法说明 在构建完成后执行的自定义脚本,用于后处理操作。 通过这个接口,我们可以配置构建之后的脚本。 ```lua xpack("test") set_formats("srpm") after_buildcmd(function (package, batchcmds) -- TODO end) ``` ## on\_installcmd * 自定义安装脚本 #### 函数原型 ::: tip API ```lua on_installcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装脚本函数,参数为package和batchcmds | #### 用法说明 自定义安装脚本,用于实现特定的安装逻辑。 这回完全重写内置默认的安装脚本,包括内部对 `add_installfiles` 配置的文件的自动安装,用户需要完全自己处理所有的安装逻辑。 ## after\_installcmd * 添加安装之后的脚本 #### 函数原型 ::: tip API ```lua after_installcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装后脚本函数,参数为package和batchcmds | #### 用法说明 在安装完成后执行的自定义脚本,用于后处理操作。 它不会重写整个安装脚本,但是会在现有的安装脚本执行之后,新增一些自定义的安装脚本: ```lua xpack("test") after_installcmd(function (package, batchcmds) batchcmds:mkdir(package:installdir("resources")) batchcmds:cp("src/assets/*.txt", package:installdir("resources"), {rootdir = "src"}) batchcmds:mkdir(package:installdir("stub")) end) ``` 需要注意的是,通过 `batchcmds` 添加的 cp, mkdir 等命令都不会被立即执行,而是仅仅生成一个命令列表,后面实际生成包的时候,会将这些命令,翻译成打包命令。 ## before\_uninstallcmd * 添加卸载之前的脚本 #### 函数原型 ::: tip API ```lua before_uninstallcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载前脚本函数,参数为package和batchcmds | #### 用法说明 在卸载之前执行的自定义脚本,用于准备卸载环境。 跟 before\_installcmd 类似,请参考 before\_installcmd 说明。 ## on\_uninstallcmd * 自定义卸载脚本 #### 函数原型 ::: tip API ```lua on_uninstallcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载脚本函数,参数为package和batchcmds | #### 用法说明 自定义卸载脚本,用于实现特定的卸载逻辑。 跟 on\_installcmd 类似,请参考 on\_installcmd 说明。 ## after\_uninstallcmd * 添加卸载之后的脚本 #### 函数原型 ::: tip API ```lua after_uninstallcmd(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载后脚本函数,参数为package和batchcmds | #### 用法说明 在卸载完成后执行的自定义脚本,用于后处理操作。 跟 after\_installcmd 类似,请参考 after\_installcmd 说明。 ## set\_nsis\_displayicon * 设置 NSIS 的显示图标 #### 函数原型 ::: tip API ```lua set_nsis_displayicon(iconfile: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | iconfile | 图标文件路径字符串 | #### 用法说明 这是一个 NSIS 专有 API,可以用于配置 NSIS 的显示图标: ```lua xpack("test") set_nsis_displayicon("bin/foo.exe") ``` 我们需要配置带有 icon 的可执行文件路径,这是使得安装包的显示 icon 跟它保持一致。 这是一个可选配置,即使我们不配置它,xmake 也会默认使用被关联的 target 中的可执行文件中图标。 --- --- url: /zh/guide/basic-commands/pack-programs.md --- # 打包程序 {#pack-programs} Xmake 主要提供下面三种打包方式,用于将目标程序进行对外分发。 ## 生成本地包 {#local-package} 通过 `xmake package` 命令,我们可以生成一个带有 xmake.lua 配置文件的本地包,它里面包含了所有的目标程序二进制文件,并且可以通过 `add_requires` 包管理接口引入和使用。 包目录结构如下: ```sh tree build/packages/f/foo/ build/packages/f/foo/ ├── macosx │   └── x86_64 │   └── release │   ├── include │   │   └── foo.h │   └── lib │   └── libfoo.a └── xmake.lua ``` 一般用于分发二进制的库,以及本地集成使用,更多详细介绍,可以看下文档:[使用本地包](/zh/guide/package-management/using-local-packages)。 此外,这种方式在配合 [内置的宏插件](/zh/guide/extensions/builtin-plugins#builtin-macro-scripts) 可以实现 ios 的 universal 二进制打包。 ```sh $ xmake macro package -p iphoneos -a "arm64,x86_64" $ tree ./build/foo.pkg/ ./build/foo.pkg/ ├── iphoneos │   ├── arm64 │   │   └── lib │   │   └── release │   │   └── libfoo.a │   ├── universal │   │   └── lib │   │   └── release │   │   └── libfoo.a │   └── x86_64 │   └── lib │   └── release │   └── libfoo.a └── xmake.lua ``` ## 生成远程包 {#remote-package} 我们也可以通过 `xmake package -f` 命令去生成远程包,用于提交到仓库分发,这种包类似本地包,也有一个 xmake.lua 配置文件,不过区别在于,它不直接存储二进制库,仅仅只有一个配置文件。 我们可以将这个包配置文件,提交到 [xmake-repo](https://github.com/xmake-io/xmake-repo) 官方仓库进行分发,也可以提交到自建的私有仓库中。 ::: tip 注意 不过生成的配置文件,也许不能直接可用,它只是生成一个大概的模板,具体还是需要用户自己编辑修改,调整对应的安装和测试逻辑。 ::: 具体详情,我们可以查看文档:[生成远程包](/zh/guide/package-management/package-distribution#generate-remote-package)。 ## 生成安装包 (XPack) {#xpack} 最后这种打包方式最为强大,通过 `xmake pack` 插件命令实现,它对标 CMake 的 CPack 打包,可以提供各种系统安装包的打包,来实现对目标程序的分发。 * Windows NSIS 二进制安装包 * Windows WIX 二进制安装包 * runself (shell) 自编译安装包 * zip/tar.gz 二进制包 * zip/tar.gz 源码包 * RPM 二进制安装包 * SRPM 源码安装包 * DEB 二进制安装包 它提供的完善的打包配置机制,可以编写灵活的配置脚本,进行更加定制化的生成安装包,不仅仅是二进制包,还有源码包,自编译安装包,归档包都能同时支持。 例如生成 Windows NSIS 安装包。 ```sh $ xmake pack -f nsis ``` ![](/assets/img/manual/nsis_3.png) 更多详情,请查看文档:[XPack 打包](/zh/guide/extensions/builtin-plugins#xpack)。 --- --- url: /zh/api/description/specification.md --- # 接口规范 ## 命名规范 {#naming-conventions} 接口的命名,是有按照预定义的一些规范来命名的,这样更加方便理解和易于使用,目前命名按照如下一些规则: | 接口规则 | 描述 | | ----------------------- | ------------------------------------------------------------ | | `is_`, `has_`前缀的接口 | 表示为条件判断 | | `set_`前缀的接口 | 表示为覆盖设置 | | `add_`前缀的接口 | 表示为追加设置 | | `s`后缀的接口 | 表示支持多值传入,例如:`add_files("*.c", "test.cpp")` | | `on_`前缀的接口 | 表示为覆盖内置脚本 | | `before_`前缀的接口 | 表示为在内置脚本运行前,执行此脚本 | | `after_`前缀的接口 | 表示为在内置脚本运行后,执行此脚本 | | `scope("name")`的接口 | 表示为定义一个描述域,例如:`target("xxx")`, `option("xxx")` | | 描述域/描述设置 | 建议缩进表示 | --- --- url: /zh/api/description/plugin-and-task.md --- # 插件任务 {#plugin-and-task} Xmake 可以实现自定义任务或者插件,其两者的核心就是`task`任务,其两者实际上是一样的,xmake的插件都是用`task`实现的。 本质上都是任务,只是[set\_category](#set_category)分类不同而已。 ## task * 定义插件或者任务 #### 函数原型 ::: tip API ```lua task(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 任务名称字符串 | #### 用法说明 `task`域用于描述一个自定义的任务实现,与[target](/zh/api/description/project-target)和[option](/zh/api/description/configuration-option)同级。 例如,这里定义一个最简单的任务: ```lua task("hello") -- 设置运行脚本 on_run(function () print("hello xmake!") end) ``` 这个任务只需要打印`hello xmake!`,那如何来运行呢? 由于这里没有使用[set\_menu](#set_menu)设置菜单,因此这个任务只能再`xmake.lua`的自定义脚本或者其他任务内部调用,例如: ```lua target("test") after_build(function (target) -- 导入task模块 import("core.project.task") -- 运行hello任务 task.run("hello") end) ``` 在构建完`test`目标后运行`hello`任务。 ## task\_end * 结束定义插件或任务 #### 函数原型 ::: tip API ```lua task_end() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 这是一个可选api,显示离开选项作用域,用法和[target\_end](/zh/api/description/project-target#target-end)类似。 ## set\_menu * 设置任务菜单 #### 函数原型 ::: tip API ```lua set_menu(options:
) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | options | 菜单选项表 | #### 用法说明 通过设置一个菜单,这个任务就可以开放给用户自己通过命令行手动调用,菜单的设置如下: ```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](#set_category)设置分类为`plugin`,那么这个任务就是一个插件了: ``` Plugins: ... echo Echo the given info! ``` 想要手动运行这个任务,可以执行: ```sh $ xmake echo hello xmake! ``` 就行了,如果要看这个任务定义的菜单,只需要执行:`xmake echo [-h|--help]`,显示结果如下: ```sh 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. ``` ::: tip 注意 其中菜单最开头的部分选项,是xmake内置的常用选项,基本上每个任务都会用到,不需要自己额外定义,简化菜单定义。 ::: 下面,我们来实际运行下这个任务,例如我要显示红色的`hello xmake!`,只需要: ```sh $ xmake echo -c red hello xmake! ``` 也可以使用选项全名,并且加上高亮: ```sh $ xmake echo --color=red --bright hello xmake! ``` 最后面的可变参数列表,在`run`脚本中通过`option.get("contents")`获取,返回的是一个`table`类型的数组。 ## set\_category * 设置任务类别 #### 函数原型 ::: tip API ```lua set_category(category: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | category | 类别名称字符串 | #### 用法说明 仅仅用于菜单的分组显示,当然插件默认会用`plugin`,内置任务默认会用:`action`,但也仅仅只是个约定。 ::: tip 注意 你可以使用任何自己定义的名字,相同名字会分组归类到一起显示,如果设置为`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`分组显示,代表普通任务。 ## on\_run * 设置任务运行脚本 #### 函数原型 ::: tip API ```lua on_run(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 任务执行函数 | #### 用法说明 可以有两种设置方式,最简单的就是设置内嵌函数: ```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](/zh/api/scripts/builtin-modules/import)导入各种扩展模块,实现复杂功能,例如: ```lua -- 导入参数选项模块 import("core.base.option") -- 入口函数 function main(...) -- 获取参数内容 print("color: %s", option.get("color")) end ``` 你也可以在当前目录下,创建多个自定义的模块文件,通过[import](/zh/api/scripts/builtin-modules/import)导入后使用,例如: ``` projectdir - xmake.lua - main.lua - module.lua ``` `module.lua`的内容如下: ```lua -- 定义一个导出接口 function hello() print("hello xmake!") end ``` ::: tip 注意 私有接口,通过`_hello`带下滑线前缀命名,这样导入的模块就不会包含此接口,只在模块自身内部使用。 ::: 然后在`main.lua`进行调用: ```lua import("module") function main(...) module.hello() end ``` 更多模块介绍见:[内置模块](/zh/api/scripts/builtin-modules/import)和[扩展模块](/zh/api/scripts/extension-modules/core/base/option) 其中,`main(...)`中参数,是通过`task.run`指定的,例如: ```lua task.run("hello", {color="red"}, arg1, arg2, arg3) ``` 里面的`arg1, arg2`这些就是传入`hello`任务`main(...)`入口的参数列表,而`{color="red"}`用来指定任务菜单中的参数选项。 更加详细的`task.run`描述,见:[task.run](#task-run) --- --- url: /zh/posts/plugin-lua.md --- xmake里面的lua脚本加载插件,可以让你方便调试和编写一些自定义的lua脚本,这个时候xmake就是一个纯lua的加载引擎。。 例如,我想写个简单的`hello xmake!`的lua脚本,可以自己建个 `hello.lua` 文件,编写如下脚本: ```lua function main() print("hello xmake!") end ``` `main`是入口函数,就跟平常写c类似,然后加载执行下这个lua脚本就行了: ```bash xmake lua /home/xxx/hello.lua or xmake l /tmp/hello.lua ``` 下面在来个稍微高级点的,我要传递参数进来,可以这么写: ```lua function main(argv) -- 打印所有参数值 for _, value in ipairs(argv) do print(value) end -- 或者可以直接dump所有 table.dump(argv) end ``` 然后我们执行下,试试: ```bash xmake lua /tmp/hello.lua hello xmake ``` 简单吧,当然除了lua内置的大部分模块,例如:`os, string, table, ...` 等等,xmake的lua加载器还提供其他更丰富的模块类库 可以通过 `import` 导入后使用,所有导入的模块支持跟插件开发中使用的类库是一样的,具体类库的使用,请参考:[插件开发之类库使用](https://xmake.io/zh/) 下面我主要介绍下,`xmake lua`插件提供的一些内置shell脚本,这些脚本是linux下的一些shell工具子集,用于实现跨平台执行,例如: ```bash xmake lua cat xmake lua cp xmake lua echo xmake lua mv xmake lua rm xmake lua rmdir xmake lua mkdir ``` 现在提供的工具不是很多,但是以后可以慢慢扩充,扩展起来还是很方便的,只需要把对应脚本:`cat.lua` 放到 `xmake lua`插件目录的 scripts 目录下就行了。。 例如我想要跨平台运行 cat 命令,可以这么执行: ```bash xmake lua cat /tmp/a or xmake l cat c:\\a.txt ``` 等以后工具扩充的越来越多,相当于在各个平台下,有了一套完善的linux的shell工具,方便日常开发使用 如果你想要看下xmake当前版本支持了哪些内置的shell工具,可以执行: ```bash xmake lua -l or xmake lua --list ``` --- --- url: /zh/posts/plugin-macro.md --- xmake 提供了一些内置的比较实用的插件,其中宏脚本插件是最具有代表性和实用性的,也是xmake比较推荐的一款插件,那它有哪些使用功能呢? 我们先来看下:`xmake macro --help` ``` Usage: xmake macro|m [options] [name] [arguments] Run the given macro. Options: --backtrace Print backtrace information 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 -v, --verbose Print lots of verbose information. -b, --begin Start to record macro. .e.g Record macro with name: test xmake macro --begin xmake config --plat=macosx xmake clean xmake -r xmake package xmake macro --end test -e, --end Stop to record macro. --show Show the content of the given macro. -l, --list List all macros. -d, --delete Delete the given macro. -c, --clear Clear the all macros. --import=IMPORT Import the given macro file or directory. .e.g xmake macro --import=/xxx/macro.lua test xmake macro --import=/xxx/macrodir --export=EXPORT Export the given macro to file or directory. .e.g xmake macro --export=/xxx/macro.lua test xmake macro --export=/xxx/macrodir name Set the macro name. (default: .) .e.g Run the given macro: xmake macro test Run the anonymous macro: xmake macro . arguments ... Set the macro arguments. ``` 看帮助菜单描述,它提供了一些功能: 1. 手动记录和回放多条执行过的xmake命令 2. 支持快速的匿名宏创建和回放 3. 支持命名宏的长久记录和重用 4. 支持宏脚本的批量导入和导出 5. 支持宏脚本的删除、显示等管理功能 6. 支持自定义高级宏脚本,以及参数配置 看功能还是蛮多的,那这个宏脚本主要用于哪些场景呢,比如: 我们需要编译打包各个平台的所有架构的库,如果按照每次: ```bash 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 ``` 那还是相当累人的,而且这些命令有可能需要重复执行,每次都这么敲一遍多累啊,如果像交叉编译这种,配置参数更多更复杂的情况,那么会更累 这个时候就需要宏脚本出场了,而且这些宏记录下来后,你可以导出它们,提供给其他人使用,而不需要每次叫他们如何去配置,如何去编译打包了 闲话少说,我们先来看下如何记录一个简单宏脚本。。 ```bash # 开始记录宏 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 ``` 好了,接下来我们就开始回放执行这个宏了。。 ```bash # 之前最近记录的一次匿名宏 xmake macro . ``` 匿名宏的好处就是快速记录,快速回放,如果需要长久保存,就需要给宏去个名字,也很简单: ```bash # 结束记录,并且命名为test宏 xmake macro --end test # 回放这个test宏 xmake macro test ``` 宏的管理:删除、导入、导出这些比较简单,可以敲:`xmake macro --help` 自行看下 我们来看下宏脚本记录下来的内容:`xmake macro --show test` ```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 ``` 其实就是个lua的脚本,里面你可以使用一切插件开发中使用的类库和内建api,你可以通过import导入他们来使用,并编写一些高级的宏脚本。。 更加高级的宏脚本写法可以参考:[插件使用之批量打包](https://xmake.io/zh/) --- --- url: /zh/posts/plugin-macro-package.md --- xmake提供了一些比较实用的内置宏脚本,比如 批量打包宏脚本 `xmake macro package` 这个宏脚本可以批量打包指定平台的所有架构,例如: ```bash # 批量打包当前平台的所有架构 xmake macro package # 批量打包iphoneos平台的所有架构 xmake macro package -p iphoneos # 批量打包iphoneos平台的所有架构,并且传入"-m debug"给 `xmake config` 进行打包debug版本,包输出到/tmp/output目录 xmake macro package -p iphoneos -f "-m debug" -o /tmp/output ``` 这个打包宏针对iphoneos平台,还会自动将所有arch的包打成一个universal包。 针对这个宏的详细使用说明,可以参看:`xmake macro package --help` --- --- url: /zh/guide/project-configuration/plugin-and-task.md --- # 插件和任务 {#plugin-and-task} Xmake 支持自定义任务和插件,它们都基于 `task` 任务系统实现。任务可以用于自动化构建流程、代码生成、文件处理等各种项目需求。 ## 基础概念 {#basic-concepts} * **任务 (Task)**: 自定义的构建步骤或工具,可以在项目中调用 * **插件 (Plugin)**: 特殊的任务,通常提供更复杂的功能,通过 `set_category("plugin")` 分类 * **菜单**: 通过 `set_menu()` 设置,让任务可以通过命令行直接调用 ## 创建简单任务 {#create-simple-task} ### 基本语法 ```lua task("taskname") on_run(function () -- 任务执行逻辑 print("任务执行中...") end) ``` ### 示例:Hello 任务 ```lua task("hello") on_run(function () print("hello xmake!") end) ``` 这个任务只能在 `xmake.lua` 中通过 `task.run()` 调用: ```lua target("test") after_build(function (target) import("core.project.task") task.run("hello") end) ``` ## 创建命令行任务 {#create-cli-task} ### 设置菜单 通过 `set_menu()` 可以让任务通过命令行直接调用: ```lua task("echo") on_run(function () import("core.base.option") -- 获取参数内容并显示 local contents = option.get("contents") or {} local color = option.get("color") or "black" cprint("${%s}%s", color, table.concat(contents, " ")) end) set_menu { usage = "xmake echo [options]", description = "显示指定信息", options = { {'c', "color", "kv", "black", "设置输出颜色"}, {nil, "contents", "vs", nil, "要显示的内容"} } } ``` 现在可以通过命令行调用: ```sh $ xmake echo -c red hello xmake! ``` ## 任务分类 {#task-categories} ### 设置任务分类 ```lua task("myplugin") set_category("plugin") -- 分类为插件 on_run(function () print("这是一个插件") end) ``` 分类说明: * **plugin**: 显示在 "Plugins" 分组中 * **action**: 内置任务默认分类 * **自定义**: 可以设置任意分类名称 ## 任务参数处理 {#task-parameters} ### 参数类型 ```lua task("example") on_run(function () import("core.base.option") -- 获取不同类型的参数 local verbose = option.get("verbose") -- 布尔值 local color = option.get("color") -- 键值对 local files = option.get("files") -- 多值参数 local args = {...} -- 可变参数 end) set_menu { options = { {'v', "verbose", "k", nil, "启用详细输出"}, -- 布尔选项 {'c', "color", "kv", "red", "设置颜色"}, -- 键值选项 {'f', "files", "vs", nil, "文件列表"}, -- 多值选项 {nil, "args", "vs", nil, "其他参数"} -- 可变参数 } } ``` ### 参数类型说明 * **k**: key-only,布尔值参数 * **kv**: key-value,键值对参数 * **v**: value,单值参数 * **vs**: values,多值参数 ## 在项目中使用任务 {#using-tasks-in-project} ### 构建后执行任务 ```lua target("test") set_kind("binary") add_files("src/*.cpp") after_build(function (target) import("core.project.task") -- 构建完成后运行代码生成任务 task.run("generate-code") -- 运行测试任务 task.run("run-tests") end) ``` ### 自定义构建任务 ```lua -- 代码生成任务 task("generate-code") on_run(function () print("生成代码...") -- 执行代码生成逻辑 os.exec("protoc --cpp_out=src proto/*.proto") end) -- 测试任务 task("run-tests") on_run(function () print("运行测试...") os.exec("xmake run test") end) ``` ### 文件处理任务 ```lua task("process-assets") on_run(function () import("core.base.option") local input_dir = option.get("input") or "assets" local output_dir = option.get("output") or "build/assets" -- 处理资源文件 os.mkdir(output_dir) os.cp(path.join(input_dir, "*.png"), output_dir) os.cp(path.join(input_dir, "*.json"), output_dir) print("资源文件处理完成") end) set_menu { usage = "xmake process-assets [options]", description = "处理项目资源文件", options = { {'i', "input", "kv", "assets", "输入目录"}, {'o', "output", "kv", "build/assets", "输出目录"} } } ``` ## 复杂任务示例 {#complex-task-examples} ### 示例 1:代码格式化任务 ```lua task("format") on_run(function () import("core.base.option") import("lib.detect.find_tool") local tool = find_tool("clang-format") if not tool then raise("clang-format not found!") end local files = option.get("files") or {"src/**/*.cpp", "src/**/*.h"} for _, pattern in ipairs(files) do local filelist = os.files(pattern) for _, file in ipairs(filelist) do os.execv(tool.program, {"-i", file}) print("格式化文件:", file) end end end) set_menu { usage = "xmake format [options]", description = "格式化代码文件", options = { {'f', "files", "vs", nil, "要格式化的文件模式"} } } ``` ### 示例 2:项目清理任务 ```lua task("clean-all") on_run(function () local patterns = { "build/**", "*.log", "*.tmp", "*.o", "*.a", "*.so", "*.dylib", "*.exe" } for _, pattern in ipairs(patterns) do os.tryrm(pattern) end print("项目清理完成") end) set_menu { usage = "xmake clean-all", description = "清理所有构建文件和临时文件" } ``` ## 任务调用方式 {#task-invocation} ### 1. 命令行调用 ```sh $ xmake taskname [options] [args...] ``` ### 2. 脚本中调用 ```lua import("core.project.task") -- 调用任务 task.run("taskname") -- 传递参数 task.run("taskname", {option1 = "value1"}, "arg1", "arg2") ``` ### 3. 在构建流程中调用 ```lua target("test") before_build(function (target) task.run("prepare") end) after_build(function (target) task.run("post-process") end) ``` ## 最佳实践 {#best-practices} 1. **错误处理**: 使用 `pcall` 包装任务逻辑 2. **进度显示**: 使用 `progress.show()` 显示执行进度 3. **参数验证**: 检查必要参数是否存在 4. **模块化**: 复杂任务使用独立模块文件 5. **文档化**: 为任务添加清晰的描述和用法说明 ## 更多信息 {#more-information} * 完整的 API 文档:[插件任务 API](/zh/api/description/plugin-and-task) * 内置任务参考:[内置插件](/zh/guide/extensions/builtin-plugins) * 插件开发指南:[插件开发](/zh/guide/extensions/plugin-development) --- --- url: /zh/guide/extensions/plugin-development.md --- # 插件开发 {#plugin-development} ## 简介 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: 生成工程文件的插件,目前已经支持 make、cmake、ninja、xcode(需要 cmake)和 vs 的工程文件,以及 compile\_commands.json 和 compile\_flags.txt 文件的生成 ## 快速开始 接下来我们介绍下本文的重点,一个简单的 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 = {} } ``` 这个插件的文件结构如下: ``` plugins |-- hello | |-- xmake.lua |... | plugins目录下无需xmake.lua ``` 现在一个最简单的插件写完了,那怎么让它被xmake检测到呢,有三种方式: 1. 把 hello 这个文件夹放置在 xmake 的插件安装目录 `xmake/plugins`,这个里面都是一些内建的插件 2. 把 hello 文件夹放置在 `~/.xmake/plugins` 用户全局目录,这样对当前 xmake 全局生效 3. 把 hello 文件夹放置在当前工程的 `./plugins` 目录下,通过在工程描述文件 xmake.lua 中调用 `add_plugindirs("plugins")` 添加当前工程的插件搜索目录,这样只对当前工程生效 ## 运行插件 接下来,我们尝试运行下这个插件: ```sh xmake hello ``` 显示结果: ``` hello xmake! ``` 最后我们还可以在 target 自定义的脚本中运行这个插件: ```lua target("demo") -- 构建之后运行插件 after_build(function (target) -- 导入task模块 import("core.project.task") -- 运行插件任务 task.run("hello") end) ``` --- --- url: /zh/posts/api-import.md --- import的主要用于导入xmake的扩展类库以及一些自定义的类库模块,一般用于 自定义脚本(on\_build, on\_run ..)、插件开发、模板开发、平台扩展、自定义任务task等地方。 导入机制如下: 1. 优先从当前脚本目录下导入 2. 再从扩展类库中导入 导入的语法规则: 基于.的类库路径规则,例如: 导入core核心扩展模块 ```lua import("core.base.option") import("core.project") import("core.project.task") import("core") function main() -- 获取参数选项 print(option.get("version")) -- 运行任务和插件 task.run("hello") project.task.run("hello") core.project.task.run("hello") end ``` 导入当前目录下的自定义模块: 目录结构: ```lua 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}) ``` 这样导入的不是这个模块的引用,而是导入的这个模块的所有公有接口本身,这样就会跟当前模块的接口进行合并,实现模块间的继承 --- --- url: /zh/posts/plugin-arguments.md --- 我们继续以之前讲解的hello插件为基础,现在为其增加参数配置选项,并且指定一个独立的脚本文件中进行开发,这样我们就可以写一些更复杂的插件 ```lua -- 定义一个名叫hello的插件任务 task("hello") -- 设置类型为插件 set_category("plugin") -- 插件运行的入口,这里指定main,说明从当前插件目录的main.lua脚本中加载插件入口 on_run("main") -- 设置插件的命令行选项,这里没有任何参数选项,仅仅显示插件描述 set_menu({ -- usage usage = "xmake hello [options]" -- description , description = "Hello xmake!" -- 定义两个参数选项 -- xmake hello --output="xxx" 指定输出的内容 -- xmake hello -v 显示插件版本 , options = { -- 第一个值设置简写:xmake hello -o xxx -- 第二个值设置全称:xmake hello --output=xxx -- 第三个值设置类型:kv是键值对,k是仅有key没有值(-v --version),v是值类型没有key -- 第四个值指定参数描述信息 {'o', "output", "kv", nil, "Set the output content." } , {'v', "version", "k", "1.0", "Show the version." } } }) ``` 这个插件的文件结构如下: ``` hello - xmake.lua - main.lua ``` xmake.lua为插件的描述文件,指定一些描述信息,main.lua为插件运行入口,代码如下: ```lua -- 导入选项模块 import("core.base.option") -- main.lua入口函数 function main() -- 显示版本? if option.get("version") then print("version: %s", option.get("version")) else -- 显示内容 print("hello %s!", option.get("output") or "xmake") end end ``` 到此一个稍微高级些插件就完成了,我们只需要执行: ```bash xmake hello --version xmake hello -v ``` 来显示版本,执行: ```bash xmake hello -o xxx xmake hello --output=xxx ``` 来显示内容,或者执行: ```bash xmake hello -h xmake hello --help ``` 来显示菜单,这个选项是内置的,不需要自定义 其中,我们用到了[import](https://xmake.io/zh/)这个api,这个api主要用于导入一些扩展的类库,实现一些高级的功能 并且还可以导入一些自定义的模块,例如我想在当前这个插件目录下新增一个模块 echo 用于回显信息,可以在hello目录下增加一个脚本文件: ``` hello - xmake.lua - main.lua - echo.lua ``` echo.lua的内容如下: ```lua -- 增加一个显示信息的接口show function show(info) print(info) end ``` 然后在main.lua里面导入这个模块就可以使用了: ```lua -- 导入选项模块 import("core.project.option") -- 导入当前插件目录下echo模块 import("echo") -- main.lua入口函数 function main() -- 使用echo模块来显示 if option.get("version") then echo.show("version: %s", option.get("version")) else -- 显示内容 echo.show("hello %s!", option.get("output") or "xmake") end end ``` 怎么样,简单吧import后,就可以直接使用这个模块的所有公有接口,像show就是被导出的公有接口 如果一些接口是私有的不想被导出怎么办呢,只需要加上 \_ 前缀就行了,例如: ```lua -- 私有接口 function _print(info) print(info) print(_g.info) end -- 公有接口 function show(info) _print(info) _g.info = info end ``` 注:其中\_g是全局私有变量,用于模块内部全局私有数据的维护和传递 --- --- url: /zh/posts/plugin-print-colors.md --- xmake在开发插件脚本的时候,除了可以使用lua内置的print进行终端输出外,还可以通过另外一个接口:`cprint`实现终端的色彩高亮输出 例如: ```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') ``` 显示结果如下: ![cprint\_colors](/assets/img/posts/xmake/cprint_colors.png) 跟颜色相关的描述,都放置在 `${ }` 里面,可以同时设置多个不同的属性,例如: ```lua ${bright red underline onyellow} ``` 表示:高亮红色,背景黄色,并且带下滑线 所有这些描述,都会影响后面一整行字符,如果只想显示部分颜色的文字,可以在结束位置,插入`${clear}`清楚前面颜色描述 例如: ```lua ${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的手势符号,是不是很炫哈。。 ![cprint\_emoji](/assets/img/posts/xmake/cprint_emoji.png) 所有的emoji表情,以及xmake里面对应的key,都可以通过[emoji符号](http://www.emoji-cheat-sheet.com/)里面找到。。 最后再来张,高亮版本的xmake主菜单界面,嘿嘿。。 ![mainmenu](/assets/img/posts/xmake/mainmenu.png) --- --- url: /zh/posts/config-files-options.md --- 之前的版本对编译控制粒度,只能到target这一级: ```lua -- 全局根配置,所有target都会被影响 add_defines("ROOT") target("test") -- target目标配置,只对test目标下的所有源文件编译生效 add_defines("TEST") add_files("src/*.c") ``` 最近给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`。 针对`add_files`的更多描述,见[手册文档](https://xmake.io/zh/),大家可以下载master版本来预先体验这一新特性。 --- --- url: /zh/api/description/conditions.md --- # 条件判断 条件判断的 API,一般用于必须要处理特定平台的编译逻辑的场合,他们通常跟 lua 的 if 语句配合使用。 ## is\_os ### 判断当前构建目标的操作系统 #### 函数原型 ::: tip API ```lua is_os(os: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | os | 操作系统名称字符串 | | ... | 可变参数,可传递多个操作系统名称 | #### 用法说明 ```lua -- 如果当前操作系统是ios if is_os("ios") then add_files("src/xxx/*.m") end ``` 目前支持的操作系统有: * windows * linux * android * macosx * ios ## is\_arch ### 判断当前编译架构 #### 函数原型 ::: tip API ```lua is_arch(arch: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | arch | 架构名称字符串 | | ... | 可变参数,可传递多个架构名称 | #### 用法说明 用于检测编译配置:`xmake f -a armv7` ```lua -- 如果当前架构是x86_64或者i386 if is_arch("x86_64", "i386") then add_files("src/xxx/*.c") end -- 如果当前平台是armv7, arm64, armv7s, armv7-a if is_arch("armv7", "arm64", "armv7s", "armv7-a") then -- ... end ``` 如果像上面那样一个个去判断所有arm架构,也许会很繁琐,毕竟每个平台的架构类型很多,xmake提供了比[add\_files](#targetadd_files)更强的lua正则表达式匹配模式,来更加简洁的进行判断: ```lua --如果当前平台是arm平台 if is_arch("arm.*") then -- ... end ``` 用`.*`就可以匹配所有了。 ## is\_plat ### 判断当前编译平台 #### 函数原型 ::: tip API ```lua is_plat(plat: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | plat | 平台名称字符串 | | ... | 可变参数,可传递多个平台名称 | #### 用法说明 用于检测编译配置:`xmake f -p iphoneos` ```lua -- 如果当前平台是android if is_plat("android") then add_files("src/xxx/*.c") end -- 如果当前平台是macosx或者iphoneos if is_plat("macosx", "iphoneos") then add_frameworks("Foundation") end ``` 目前支持的平台有: * windows * cross * linux * macosx * android * iphoneos * watchos 当然你也可以自己扩展添加自己的平台,甚至直接指定自己的平台名: ```sh $ xmake f -p other --sdk=... ``` 如果指定的平台名不存在,就会自动切到`cross`平台进行交叉编译,但是却可以通过`is_plat("other")`来判断自己的平台逻辑。 ## is\_host ### 判断当前主机环境的操作系统 #### 函数原型 ::: tip API ```lua is_host(host: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | host | 主机系统名称字符串 | | ... | 可变参数,可传递多个主机名称 | #### 用法说明 有些编译平台是可以在多个不同的操作系统进行构建的,例如:android的ndk就支持linux,macOS还有windows环境。 这个时候就可以通过这个接口,区分当前是在哪个系统环境下进行的构建。 ```lua -- 如果当前主机环境是windows if is_host("windows") then add_includedirs("C:\\includes") else add_includedirs("/usr/includess") end ``` 目前支持的主机环境有: * windows * linux * macosx 你也可以通过[$(host)](/zh/api/description/builtin-variables#var-host)内置变量或者[os.host](/zh/api/scripts/builtin-modules/os#os-host)接口,来进行获取 ## is\_subhost ### 判断当前主机的子系统环境 #### 函数原型 ::: tip API ```lua is_subhost(subhost: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | subhost | 子系统名称字符串 | | ... | 可变参数,可传递多个子系统名称 | #### 用法说明 目前主要用于 windows 系统上 cygwin, msys2 等子系统环境的探测,如果在 msys2 shell 环境下运行 xmake,那么 `is_subhost("windows")` 想将会返回 false,而 `is_host("windows")` 依旧会返回 true。 目前支持的子系统: * msys * cygwin 配置例子: ```lua if is_subhost("msys", "cygwin") then -- 当前在 msys2/cygwin 的 shell 环境下 end ``` 我们也可以通过执行 `xmake l os.subhost` 来快速查看当前的子系统平台。 ::: tip 提示 后期也有可能会支持 linux 和 macos 系统下的其他子系统环境,如果存在话。 ::: ## is\_subarch ### 判断当前主机子系统环境下的架构 #### 函数原型 ::: tip API ```lua is_subarch(subarch: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | subarch | 子系统架构名称字符串 | | ... | 可变参数,可传递多个子系统架构名称 | #### 用法说明 目前主要用于 windows 系统上 cygwin, msys2 等子系统环境下架构的探测,通常在 windows 编译平台采用 msvc 工具链,那边编译架构时 x64,x86。 而在 msys/cygwin 子系统环境下,编译架构默认为 x86\_64/i386,是有差异的。 我们也可以通过执行 `xmake l os.subarch` 来快速查看当前的子系统架构。 ## is\_cross ### 判断当前平台是否为交叉编译 #### 函数原型 ::: tip API ```lua is_cross() ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | - | 无参数 | #### 用法说明 如果当前的目标架构和平台,不是当前的主机平台,属于交叉编译,这个接口就会返回 true。 ## is\_mode ### 判断当前编译模式 #### 函数原型 ::: tip API ```lua is_mode(mode: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | mode | 编译模式名称字符串 | | ... | 可变参数,可传递多个模式名称 | #### 用法说明 用于检测编译配置:`xmake f -m debug` 编译模式的类型并不是内置的,可以自由指定,一般指定:`debug`, `release`, `profile` 这些就够用了,当然你也可以在xmake.lua使用其他模式名来判断。 ```lua -- 如果当前编译模式是debug if is_mode("debug") then add_defines("DEBUG") set_symbols("debug") set_optimize("none") end -- 如果是release模式 if is_mode("release") then set_symbols("hidden") set_strip("all") end ``` ## is\_kind ### 判断当前编译类型 #### 函数原型 ::: tip API ```lua is_kind(kind: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | kind | 目标类型名称字符串 | | ... | 可变参数,可传递多个类型名称 | #### 用法说明 判断当前是否编译的是动态库还是静态库,用于检测编译配置:`xmake f -k [static|shared]` 一般用于如下场景: ```lua target("test") -- 通过配置设置目标的kind set_kind("$(kind)") add_files("src/*c") -- 如果当前编译的是静态库,那么添加指定文件 if is_kind("static") then add_files("src/xxx.c") end ``` 编译配置的时候,可手动切换,编译类型: ```sh # 编译静态库 $ xmake f -k static $ xmake ``` ```sh # 编译动态库 $ xmake f -k shared $ xmake ``` ## is\_config ### 判断指定配置是否为给定的值 #### 函数原型 ::: tip API ```lua is_config(name: , values: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置选项名称字符串 | | values | 配置值字符串 | | ... | 可变参数,可传递多个值 | #### 用法说明 此接口从2.2.2版本开始引入,用于判断指定配置是否为给定的值,可用于描述域。 例如: ```sh $ 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 -- 根据lua_flavor的配置值,选择依赖lua还是luajit option("lua_flavor") set_showmenu(true) set_values("luajit", "lua") option_end() if is_config("lua_flavor", "luajit") then add_requires("luajit") elseif is_config("lua_flavor", "lua") then add_requires("lua") end ``` 不仅如此,我们还可以设置模式匹配规则去判断值,例如: ```lua -- 如果自定义的test配置值带有hello前缀 if is_config("test", "hello.*") then add_defines("HELLO") end ``` ::: tip 提示 此接口不仅能够判断通过[option](/zh/api/description/configuration-option#option)定义的自定义配置选项,同时还能判断内置的全局配置、本地配置。 ::: ## has\_config ### 判断配置是否启用或者存在 #### 函数原型 ::: tip API ```lua has_config(configs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | configs | 配置名称字符串 | | ... | 可变参数,可传递多个配置名称 | #### 用法说明 此接口从2.2.2版本开始引入,用于检测自定义或者内置的编译配置是否存在或启用,可用于描述域。 例如以下配置情况,都会返回true: ```sh # 启用某个配置选项(如果是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: ```sh # 禁用配置(如果是boolean类型配置) $ xmake f --test1=n $ xmake f --test1=no $ xmake f --test1=false ``` ::: tip 提示 此接口不仅能够判断内置的全局配置、本地配置,同时还可以判断通过[option](/zh/api/description/configuration-option#option)定义的自定义配置选项。 此接口与[get\_config](/zh/api/description/global-interfaces#get-config)接口配合使用,可以完整地获取和判断用户通过`xmake f --option1=xxx`设置的选项状态。 ::: ## has\_package ### 判断依赖包是否启用或者存在 #### 函数原型 ::: tip API ```lua has_package(packages: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | packages | 包名称字符串 | | ... | 可变参数,可传递多个包名称 | #### 用法说明 此接口从2.2.3版本开始引入,用于检测远程依赖包是否存在或启用,可用于描述域。 一般配合[add\_requires](/zh/api/description/global-interfaces#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定义甚至源文件编译控制做一些特殊处理。 ::: tip 注意 此接口跟[has\_config](#has_config)的区别在于,[has\_config](#has_config)用于[option](/zh/api/description/configuration-option#option),而它用于[add\_requires](/zh/api/description/global-interfaces#add-requires)。 ::: --- --- url: /zh/guide/basic-commands/build-targets.md --- # 构建目标 {#build-targets} 之前我们已经简单讲过,可以通过 `xmake build` 命令来构建工程。 这里我们再来详细讲解下,首先,我们先看下它的完整命令格式。 ## 命令格式 ```sh $ xmake build [options] [target] ``` 其中,`[target]` 指定需要构建的 target 目标,这是可选的,如果没有设置,那么默认会构建所有的目标程序(被标记为 default = false 的除外)。 执行效果如下: ```sh $ xmake build [ 17%]: cache compiling.release src/main.cpp [ 23%]: cache compiling.release src/foo.cpp [ 35%]: linking.release libfoo.a [ 71%]: linking.release test [100%]: build ok, spent 1.173s ``` 通常我们可以省略后面的 `build` 子命令,因为 xmake 命令的默认行为就是执行构建。 ```sh $ xmake [ 17%]: cache compiling.release src/main.cpp [ 23%]: cache compiling.release src/foo.cpp [ 35%]: linking.release libfoo.a [ 71%]: linking.release test [100%]: build ok, spent 1.173s ``` ## 构建特定目标 如果要指定构建特定的目标程序,可以执行: ```sh $ xmake build foo ``` :::tip 注意 这个时候,需要写全 build 子命令,否则目标名可能会跟其他子命令冲突。 ::: ## 重新构建目标 ```sh $ xmake -r ``` 或者 ```sh $ xmake --rebuild ``` 都可以实现强制重新编译目标程序。 ## 构建全部目标程序 如果 target 被配置为 `default = false`,那么默认是不会编译它的。 ```lua target("test") set_default(false) add_files("src/*.c") ``` 如果想要构建所有目标, 包括这些 `default = false` 的目标程序,那么可以传递 `-a/--all` 参数。 ```sh $ xmake build -a ``` 或者 ```sh $ xmake build --all ``` ## 查找详细编译命令 如果我们想查看完整的编译器命令参数,来排查 flags 配置等问题,可以使用 `xmake -v`。 ```sh $ xmake -v [ 23%]: cache compiling.release src/main.cpp /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.2 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp [ 47%]: linking.release test /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -o build/macosx/x86_64/release/test build/.objs/test/macosx/x86_64/release/src/main.cpp.o -target x86_64-apple-macos15.2 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -lz -Wl,-x -Wl,-dead_strip [100%]: build ok, spent 0.585s ``` ## 查看错误日志和堆栈 如果在编译过程中遇到问题,构建失败了,会默认显示编译错误,但是如果配置上有语法错误,并不会显示完整的堆栈。 如果想要进一步定位问题,可以通过下面的命令,查看完整的构建日志,包括配置堆栈。 ```lua add_rules("mode.debug", "mode.release") target("foo") set_kind("static") add_files("src/foo.cpp") target("test") set_kind("binary") dd_deps("foo") --------------- 不正确的接口 add_files("src/main.cpp") ``` ```sh $ xmake -vD error: @programdir/core/main.lua:329: @programdir/core/sandbox/modules/import/core/base/task.lua:65: @progr amdir/core/project/project.lua:1050: ./xmake.lua:9: attempt to call a nil value (global 'dd_deps') stack traceback: [./xmake.lua:9]: in main chunk ----------------- 实际配置错误的地方 stack traceback: [C]: in function 'error' @programdir/core/base/os.lua:1075: in function 'os.raiselevel' (...tail calls...) @programdir/core/main.lua:329: in upvalue 'cotask' @programdir/core/base/scheduler.lua:406: in function <@programdir/core/base/scheduler.lua:399> ``` --- --- url: /zh/guide/project-configuration/add-packages.md --- # 添加依赖包 {#add-packages} ## 简介 Xmake 内置支持包依赖集成,可以通过 [add\_requires](/zh/api/description/global-interfaces.html#add-requires) 接口声明需要的依赖包。 然后通过 [add\_packages](/zh/api/description/project-target.html#add-packages) 接口,将声明的包绑定到需要的编译目标中去,例如: ```lua [xmake.lua] add_requires("tbox 1.6.*", "libpng ~1.16", "zlib") target("foo") set_kind("binary") add_files("src/*.c") add_packages("tbox", "libpng") target("bar") set_kind("binary") add_files("src/*.c") add_packages("zlib") ``` 其中 `add_requires` 是全局接口,用于包的配置声明,Xmake 会根据声明的包来触发查找安装。 由于一个工程,可能存在多个目标程序,每个目标程序可能需要的依赖包是不同的,因此我们还需要通过 `add_packages` 来将绑定目标。 上面的配置示例中,foo 目标绑定了 tbox 和 libpng 两个包,而 bar 目标绑定了 zlib 包。 ## 基本用法与常见场景 {#basic-usage} * `add_requires("pkgname")` 声明依赖包,支持版本、可选、别名等 * `add_packages("pkgname")` 绑定包到目标,自动追加 links、includedirs 等 ### 典型场景 * 多目标分别依赖不同包 * 同一目标依赖多个包 * 支持 C/C++/Fortran/多平台 ## API 详解 {#api-details} ### 指定包版本 ```lua add_requires("tbox 1.6.*", "libpng ~1.16", "zlib") ``` ### 可选包 ```lua add_requires("foo", {optional = true}) ``` ### 禁用系统库 ```lua add_requires("foo", {system = false}) ``` ### 指定别名 ```lua add_requires("foo", {alias = "myfoo"}) add_packages("myfoo") ``` ### 平台/架构限定 ```lua add_requires("foo", {plat = "windows", arch = "x64"}) ``` ### 传递包配置参数 ```lua add_requires("tbox", {configs = {small = true}}) ``` ### 传递给依赖包 ```lua add_requireconfs("spdlog.fmt", {configs = {header_only = true}}) ``` ## 包依赖的高级特性 {#advanced-features} * 支持语义版本、分支、commit * 支持 debug/release 依赖包 * 支持多仓库、私有仓库 * 支持包的本地/系统/远程优先级 * 支持包的额外编译参数 ## 包实例接口 {#package-instance} 在自定义规则、after\_install 等脚本中可用: * `package:name()` 获取包名 * `package:version_str()` 获取包版本 * `package:installdir()` 获取包安装目录 * `package:get("links")` 获取链接库 * `package:get("includedirs")` 获取头文件目录 ## 典型示例 {#examples} ### 1. 依赖可选包 ```lua add_requires("foo", {optional = true}) target("bar") add_packages("foo") ``` ### 2. 依赖指定分支/commit ```lua add_requires("tbox master") add_requires("zlib 1.2.11") ``` ### 3. 传递参数给包 ```lua add_requires("spdlog", {configs = {header_only = true}}) ``` ### 4. 依赖本地包 1. 在工程目录下新建本地包仓库目录(如 `local-repo/packages/foo/xmake.lua`)。 2. 在 `xmake.lua` 中添加本地仓库: ```lua add_repositories("myrepo local-repo") add_requires("foo") ``` 3. 本地包描述文件结构示例: ``` local-repo/ packages/ foo/ xmake.lua ``` 4. 这样即可像官方包一样通过 `add_requires("foo")` 使用本地包。 ## 最佳实践 {#best-practices} 1. 推荐用 `add_requires` + `add_packages` 组合声明和绑定 2. 可选包用 `{optional = true}`,提升兼容性 3. 多平台用 `{plat=..., arch=...}` 精准控制 4. 善用 `add_requireconfs` 递归配置依赖包 5. 通过 `xmake require --info pkg` 查询包参数 ## 更多信息 {#more-information} * 官方包用法:[使用官方包](/zh/guide/package-management/using-official-packages) * 包依赖 API:[包依赖 API](/zh/api/description/package-dependencies) * 包实例接口:[package 实例 API](/zh/api/scripts/package-instance) * 包管理与查找:可使用 [xrepo 命令行工具](/zh/guide/package-management/xrepo-cli) 进行包的搜索、安装、卸载等操作。 --- --- url: /zh/guide/extras/environment-variables.md --- # 环境变量 {#environment-variables} 我们可以执行下面的命令,获取所有 xmake 用到的环境变量,以及当前被设置的值: ```sh $ xmake show -l envs XMAKE_RAMDIR Set the ramdisk directory. XMAKE_GLOBALDIR Set the global config directory of xmake. /Users/ruki XMAKE_ROOT Allow xmake to run under root. XMAKE_COLORTERM Set the color terminal environment. XMAKE_PKG_INSTALLDIR Set the install directory of packages. XMAKE_TMPDIR Set the temporary directory. /var/folders/vn/ppcrrcm911v8b4510klg9xw80000gn/T/.xmake501/211104 XMAKE_PKG_CACHEDIR Set the cache directory of packages. XMAKE_PROGRAM_DIR Set the program scripts directory of xmake. /Users/ruki/.local/share/xmake XMAKE_PROFILE Start profiler, e.g. perf, trace. XMAKE_RCFILES Set the runtime configuration files. XMAKE_CONFIGDIR Set the local config directory of project. /Users/ruki/projects/personal/xmake-docs/.xmake/macosx/x86_64 XMAKE_LOGFILE Set the log output file path. ``` ## XMAKE\_RAMDIR * 设置 ramdisk 目录路径 ramdisk 目录是内存文件系统的目录位置,通常 `os.tmpdir()` 接口会用到。xmake 内部使用的临时文件,如果用户设置 ramdisk 路径,则会优先存储在这个上面,提升整体编译速度。 ## XMAKE\_TMPDIR * 设置用户的临时目录 默认 xmake 会使用 `/tmp/.xmake` 和 `%TEMP%/.xmake`,当然用户可以通过这个变量去修改默认路径。 ## XMAKE\_CONFIGDIR * 设置本地工程配置目录 每个项目的本地编译配置,默认会存储在当前项目根目录的 `.xmake` 路径下,然后根据不同的平台、架构区分,例如: ```sh .xmake/macosx/x86_64 ``` 我们如果不想存储在项目根目录,也可以自己设置到其他路径,比如 build 目录下等。 ## XMAKE\_GLOBALDIR * 设置全局配置文件根目录 xmake 会在该目录下创建 `.xmake`,作为 `xmake g/global` 全局配置的存储目录,还有安装包、缓存等其他全局文件,默认都会存储在这个目录下。 默认路径为:`~`。 ## XMAKE\_ROOT * 允许用户在 root 模式下运行 通常 xmake 默认禁止在 root 下运行,这非常不安全。但是如果用户非要在 root 下运行,也可以设置这个变量,强制开启。 ```sh export XMAKE_ROOT=y ``` ## XMAKE\_COLORTERM * 设置 Terminal 的色彩输出 目前可以设置这几个值: | 值 | 描述 | | --- | --- | | nocolor | 禁用彩色输出 | | color8 | 8 色输出支持 | | color256 | 256 色输出支持 | | truecolor | 真彩色输出支持 | 通常,用户不需要设置它们,xmake 会自动探测用户终端支持的色彩范围。如果用户不想输出色彩,可以设置 nocolor 来全局禁用。 或者用 `xmake g --theme=plain` 也可以全局禁用。 ## XMAKE\_PKG\_INSTALLDIR * 设置依赖包的安装根目录 xmake 的远程包安装的全局目录默认是 `$XMAKE_GLOBALDIR/.xmake/packages`,但是用户也可以设置这个变量,去单独修改它。 我们也可以使用 `xmake g --pkg_installdir=/xxx` 去设置它,效果是一样的。但是环境变量的优先级高于此配置。 ## XMAKE\_PKG\_CACHEDIR * 设置依赖包的缓存目录 默认路径在 `$XMAKE_GLOBALDIR/.xmake/cache` 目录,存储包安装过程中的各种缓存文件,比较占存储空间,用户也可以单独设置它。 当然,xmake 在每个月都会自动清理上个月的所有缓存文件。 ## XMAKE\_PROGRAM\_DIR * 设置 xmake 的脚本目录 xmake 的所有 lua 脚本随安装程序一起安装,默认都在安装目录下,但是如果想要切换到自己下载的脚本目录下,方便本地修改调试,可以设置此变量。 如果要查看当前 xmake 在使用的脚本目录,可以执行: ```sh $ xmake l os.programdir /Users/ruki/.local/share/xmake ``` ## XMAKE\_PROFILE * 开启性能分析 这个仅对 xmake 的开发者开放,用于分析 xmake 运行过程中的耗时情况,追踪调用过程。 ### 分析函数调用耗时 ```sh $ XMAKE_PROFILE=perf:call xmake [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! 0.238, 97.93%, 1, runloop : @programdir/core/base/scheduler.lua: 805 0.180, 74.04%, 25, _resume : [C]: -1 0.015, 6.34%, 50, _co_groups_resume : @programdir/core/base/scheduler.lua: 299 0.011, 4.37%, 48, wait : @programdir/core/base/poller.lua: 111 0.004, 1.70%, 62, status : @programdir/core/base/scheduler.lua: 71 0.004, 1.53%, 38, is_dead : @programdir/core/base/scheduler.lua: 76 0.003, 1.44%, 50, next : @programdir/core/base/timer.lua: 74 0.003, 1.33%, 48, delay : @programdir/core/base/timer.lua: 60 0.002, 1.02%, 24, is_suspended : @programdir/core/base/scheduler.lua: 86 ``` ### 分析进程耗时 可以用于分析每个文件的编译耗时,以及一些运行瓶颈。 ```sh $ XMAKE_PROFILE=perf:process xmake -r [ 7%]: compiling.release src/header.h [ 23%]: compiling.release src/test.cpp [ 30%]: compiling.release src/test8.cpp [ 38%]: compiling.release src/test4.cpp [ 46%]: compiling.release src/test5.cpp [ 53%]: compiling.release src/test7.cpp [ 61%]: compiling.release src/test6.cpp [ 69%]: compiling.release src/test2.cpp [ 76%]: compiling.release src/main.cpp [ 84%]: compiling.release test3.cpp [ 84%]: compiling.release src/test.c [ 92%]: linking.release main [100%]: build ok, spent 2.754s 1411.000, 22.19%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_37317EEDB62F4F3088AF6A2E2A649460 -fdiagnostics-color=always -x c++-header -o build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch src/header.h 508.000, 7.99%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_1ABAE1FAD68D45008DC76A3A00697820 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/main.cpp.o src/main.cpp 473.000, 7.44%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_1C0BE5280C6F4E208F919577A48AAA40 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/test3.cpp.o test3.cpp 451.000, 7.09%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_877D3D9B6BBA4D308BFB5E4EBD751340 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test6.cpp.o src/test6.cpp 404.000, 6.35%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_C9968E2873B648208A8C3F2BA7573640 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test7.cpp.o src/test7.cpp 402.000, 6.32%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_7F6DFA37FF494D208EADF9737484EC40 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test2.cpp.o src/test2.cpp 383.000, 6.02%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_63C9E23AE7E047308F762C7C02A56B50 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test4.cpp.o src/test4.cpp 374.000, 5.88%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_C3A0EF96A7C14D00879BFAEFD26E9D20 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test8.cpp.o src/test8.cpp 368.000, 5.79%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_BADB46AF75CB4610857EF5083BD54D30 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test.cpp.o src/test.cpp 363.000, 5.71%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -std=c++11 -include src/header.h -include-pch build/.objs/main/macosx/x86_64/release/src/cxx/header.h.pch -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_0247BDB87DD14500816471184D4E8140 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test5.cpp.o src/test5.cpp 156.000, 2.45%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fPIC -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F0FF8220B33B46208D39A98937D55E50 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.c 133.000, 2.09%, 3, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang --version 107.000, 1.68%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -O3 -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_C8A96266E0034C20898C147FC52F3A40 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.c 105.000, 1.65%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fdiagnostics-color=always -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_48A2FA7BE7AB44008B60558E412A9D30 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.c 105.000, 1.65%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -fPIC -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -lz -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -lz -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F510FB15C9A647108111A7010EFED240 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp 91.000, 1.43%, 3, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ --version 74.000, 1.16%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -fvisibility=hidden -O3 -DNDEBUG -MMD -MF /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_BF6B4B6DACB843008E822CEFDC711230 -fdiagnostics-color=always -o build/.objs/main/macosx/x86_64/release/src/test.c.o src/test.c 73.000, 1.15%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -o build/macosx/x86_64/release/main build/.objs/main/macosx/x86_64/release/src/test.cpp.o build/.objs/main/macosx/x86_64/release/src/test8.cpp.o build/.objs/main/macosx/x86_64/release/src/test4.cpp.o build/.objs/main/macosx/x86_64/release/src/test5.cpp.o build/.objs/main/macosx/x86_64/release/src/test7.cpp.o build/.objs/main/macosx/x86_64/release/src/test6.cpp.o build/.objs/main/macosx/x86_64/release/src/test2.cpp.o build/.objs/main/macosx/x86_64/release/src/main.cpp.o build/.objs/main/macosx/x86_64/release/test3.cpp.o build/.objs/main/macosx/x86_64/release/src/test.c.o -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -lz -Wl,-x -Wl,-dead_strip 70.000, 1.10%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fPIC -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_6D0B6327841A47208939EEF194F38B50 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp 68.000, 1.07%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -O3 -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_8AB279F8450D4D108E92951CC9C1C650 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp 65.000, 1.02%, 1, /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -fdiagnostics-color=always -Qunused-arguments -target x86_64-apple-macos15.1 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.1.sdk -S -o /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_D25F0DB04D6D430084C098F1E1F76C00 /var/folders/32/w9cz0y_14hs19lkbs6v6_fm80000gn/T/.xmake501/241220/_F3443E45635A466AA3BEAE9DE99B4339.cpp ``` ### 追踪 xmake 的运行过程 ```sh $ XMAKE_PROFILE=trace xmake func : @programdir/core/base/scheduler.lua: 457 is_suspended : @programdir/core/base/scheduler.lua: 86 status : @programdir/core/base/scheduler.lua: 71 thread : @programdir/core/base/scheduler.lua: 66 thread : @programdir/core/base/scheduler.lua: 66 length : @programdir/core/base/heap.lua: 120 ``` ### 分析运行卡死问题 可以用于获取 xmake 运行卡死时的栈。启用此特性后,通过 Ctrl+C 中断后就能获取栈。 ```sh $ XMAKE_PROFILE=stuck xmake l test.lua stack traceback: [C]: in function 'base/io.file_read' @programdir/core/base/io.lua:177: in method '_read' @programdir/core/sandbox/modules/io.lua:90: in function <@programdir/core/sandbox/modules/io.lua:89> (...tail calls...) /Users/ruki/share/test.lua:2: in function (...tail calls...) @programdir/plugins/lua/main.lua:123: in function <@programdir/plugins/lua/main.lua:79> (...tail calls...) [C]: in function 'xpcall' @programdir/core/base/utils.lua:280: in function 'sandbox/modules/utils.trycall' (...tail calls...) @programdir/core/base/task.lua:519: in function 'base/task.run' @programdir/core/main.lua:278: in upvalue 'cotask' @programdir/core/base/scheduler.lua:371: in function <@programdir/core/base/scheduler.lua:368> ``` ## XMAKE\_RCFILES * 设置全局配置文件 我们可以设置一些 xmakerc.lua 全局配置文件,在用户编译项目的时候,全局引入它们,比如全局引入一些用户自定义的帮助脚本,工具链什么的。 ```sh $ export XMAKE_RCFILES=xmakerc.lua $ xmake ``` 如果不设置此环境变量,用户可以在`/etc/xmakerc.lua`、`~/xmakerc.lua`与`$XMAKE_GLOBALDIR/.xmake/xmakerc.lua`设置全局配置文件,搜索优先级从高至低排列。 ## XMAKE\_LOGFILE * 设置日志文件路径 默认 xmake 会回显输出到终端,我们在可以通过设置这个路径,开启日志自动存储到指定文件,但它不会影响终端的正常回显输出。 ## XMAKE\_MAIN\_REPO * 设置官方包主仓库地址 xmake 默认内置了三个主仓库地址,它们是完全相同的,xmake 会根据当前网络状态选择最优的地址来使用。 ``` https://github.com/xmake-io/xmake-repo.git https://gitlab.com/tboox/xmake-repo.git https://gitee.com/tboox/xmake-repo.git ``` 但如果 xmake 选择错误,可能会导致仓库下载失败,而通过这个环境变量,我们可以自己设置固定使用指定的仓库地址,不再进行自动选择。 ```sh $ export XMAKE_MAIN_REPO = https://github.com/xmake-io/xmake-repo.git ``` ## XMAKE\_BINARY\_REPO * 设置官方包预编译仓库地址 类似 `XMAKE_MAIN_REPO`,唯一的区别是,这个用于切换预编译仓库的地址。 ```sh $ export XMAKE_BINARY_REPO = https://github.com/xmake-mirror/build-artifacts.git ``` ## XMAKE\_THEME * 设置主题 通常我们可以通过 `xmake g --theme=plain` 来设置颜色主题,但是它是全局的,如果想单独对当前终端会话设置,我们就可以使用这个环境变量来设置。 ```sh $ export XMAKE_THEME=plain ``` ## XMAKE\_STATS * 开启或禁用用户量统计 由于目前 xmake 还在发展初期,我们需要知道大概的用户量增长情况,以便于提供我们持续更新 xmake 的动力。 因此 xmake 默认每天的第一次项目构建,会在后台进程自动 git clone 一个空仓库:https://github.com/xmake-io/xmake-stats 然后借用 github 自身提供的 Traffic 统计图表来获取大概的用户量。 对于每个项目,每天只会统计一次,并且不会泄露任何用户隐私,因为仅仅只是多了一次额外的 git clone 操作,另外我们 clone 的是一个空仓库,不会耗费用户多少流量。 当然,并不是每个用户都希望这么做,用户完全有权利去禁用这个行为,我们只需要设置: ```sh export XMAKE_STATS=n ``` 就可以完全禁用它,另外我们也会在 ci 上自动禁用这个行为。 什么时候移除它? 这个行为并不会永久存在,等到 xmake 有了足够多的用户量,或者有了其他更好的统计方式,我们会考虑移除相关统计代码。 当然,如果有非常多的用户反馈不愿意接受它,我们也会考虑移除它。 关于这个的相关 issues 见:[#1795](https://github.com/xmake-io/xmake/issues/1795) --- --- url: /zh/api/scripts/target-instance.md --- # 目标实例 {#target-instance} 此页面描述了 [工程目标](/zh/api/description/project-target) 的 `on_load()`、`before_build()` 或 `after_install()` 等函数的 `target` 接口。 ## target:name * 获取目标的名字 #### 函数原型 ::: tip API ```lua target:name() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ## target:get * 获取目标在描述域的配置值 #### 函数原型 ::: tip API ```lua target:get(key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | #### 用法说明 任何在描述域的 `set_xxx` 和 `add_xxx` 配置值都可以通过这个接口获取到。 ```lua -- get the links target:get("links") -- get the defined macros target:get("defines") ``` ## target:set * 设置目标的配置值 #### 函数原型 ::: tip API ```lua target:set(key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | | value | 配置值 | #### 用法说明 如果你想添加值可以用 [target:add](#target-add)。 ```lua -- set the links target:set("links", "sdl2") -- set the defined macros target:set("defines", "SDL_MAIN_HANDLED") ``` ::: tip 注意 任何脚本域下对 `target:set("xxx", ...)` 的配置,都是完全跟描述域的 `set_xxx` 接口保持一致的,具体参数说明,可以直接参考描述域下对应的 `set_xxx` 接口说明。 例如: * 描述域:`set_kind("binary")` * 脚本域:`target:set("kind", "binary")` ::: ## target:add * 按名称添加到目标的值 #### 函数原型 ::: tip API ```lua target:add(key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | | value | 要添加的值 | #### 用法说明 ```lua -- add links target:add("links", "sdl2") -- add defined macros target:add("defines", "SDL_MAIN_HANDLED") ``` ::: tip 注意 任何脚本域下对 `target:add("xxx", ...)` 的配置,都是完全跟描述域的 `add_xxx` 接口保持一致的,具体参数说明,可以直接参考描述域下对应的 `add_xxx` 接口说明。 例如: * 描述域:`add_files("src/*.c", {defines = "PRIVATE"})` * 脚本域:`target:add("files", "src/*.c", {defines = "PRIVATE"})` ::: ## target:kind * 获取目标程序类型 #### 函数原型 ::: tip API ```lua target:kind() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 对应 `set_kind` 描述域接口设置。目标类型主要有:binary, static, shared, phony, object, headeronly。 ## target:is\_plat * 当前平台是否是给定平台之一 #### 函数原型 ::: tip API ```lua target:is_plat(plat: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | plat | 平台名称 | #### 用法说明 尽管,我们也可以用 `is_plat` 全局接口直接判断平台,但是 xmake 支持使用 `set_plat` 针对特定 target 单独设置编译平台。 这个时候,使用全局接口,就不适用了,所以通常我们推荐使用 target 提供的接口,来直接对当前 target 判断编译平台,更加可靠。 ```lua -- Is the current platform android? target:is_plat("android") -- Is the current platform windows, linux or macosx? target:is_plat("windows", "linux", "macosx") ``` ## target:is\_arch * 当前架构是否是给定架构之一 #### 函数原型 ::: tip API ```lua target:is_arch(arch: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | arch | 架构名称 | #### 用法说明 尽管,我们也可以用 `is_arch` 全局接口直接判断架构,但是 xmake 支持使用 `set_arch` 针对特定 target 单独设置编译架构。 这个时候,使用全局接口,就不适用了,所以通常我们推荐使用 target 提供的接口,来直接对当前 target 判断编译架构,更加可靠。 ```lua -- Is the current architecture x86 target:is_arch("x86") -- Is the current architecture x64 or x86_64 target:is_arch("x64", "x86_64") ``` ## target:is\_arch64 * 当前架构是否是 64 位架构 #### 函数原型 ::: tip API ```lua target:is_arch64() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua -- 当前架构是否是 64 位架构? target:is_arch64() ``` ## target:targetfile * 获取目标文件路径 #### 函数原型 ::: tip API ```lua target:targetfile() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 主要用于获取 static, shared, binary 目标程序文件的输出路径。 ```lua os.cp(target:targetfile(), "/tmp/") ``` ## target:artifactfile * 获取目标的产物文件 #### 函数原型 ::: tip API ```lua target:artifactfile(kind: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|---------| | kind | 目标类型 | #### 用法说明 目前只能获取 windows DLL 的 implib 文件输出路径。 ```lua target:artifactfile("implib") ``` 不过,后期有可能会扩展到其他类型的产物文件路径获取。 ## target:targetdir * 获取目标文件的输出目录 #### 函数原型 ::: tip API ```lua target:targetdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 也就是 target:targetfile() 对应的存储目录。 ## target:basename * 获取目标文件的 base 名 #### 函数原型 ::: tip API ```lua target:basename() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 也就是 libfoo.a,foo.dll, foo.exe 中的 `foo`。 ## target:filename * 获取目标文件名 #### 函数原型 ::: tip API ```lua target:filename() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 目标文件的完整文件名,等价于 `path.filename(target:targetfile())`。 ## target:installdir * 获取目标文件的安装目录 #### 函数原型 ::: tip API ```lua target:installdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 通常用于 `xmake install/uninstall` 的 after\_install 等脚本中获取对应的安装目录路径,可以用于用户自定义安装脚本。 ## target:autogendir * 获取自动生成目录 #### 函数原型 ::: tip API ```lua target:autogendir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 这个通常用于一些自定义规则脚本中,存放一些特定于 target 的自动生成文件,路径通常在 `build/.gens/target` 下面。 比如,我们在处理 lex/yacc 自动生成一些源码文件,就可以存放在这个目录下,方便之后去处理它。 ## target:objectfile * 获取对象文件路径 #### 函数原型 ::: tip API ```lua target:objectfile(sourcefile: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sourcefile | 源文件路径 | #### 用法说明 通常用于自定义脚本中,获取源文件对应的目标文件路径,例如 ```lua local objectfile = target:objectfile(sourcefile) ``` ## target:sourcebatches * 获取所有源文件 #### 函数原型 ::: tip API ```lua target:sourcebatches() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 它可以获取到 `add_files` 添加的所有源文件,并且根据不同源文件类型,分别存储。 大概结构如下: ```lua { "c++.build" = { objectfiles = { "build/.objs/test/macosx/x86_64/release/src/main.cpp.o" }, rulename = "c++.build", sourcekind = "cxx", sourcefiles = { "src/main.cpp" }, dependfiles = { "build/.deps/test/macosx/x86_64/release/src/main.cpp.o.d" } }, "asm.build" = { objectfiles = { "build/.objs/test/macosx/x86_64/release/src/test.S.o" }, rulename = "asm.build", sourcekind = "as", sourcefiles = { "src/test.S" }, dependfiles = { "build/.deps/test/macosx/x86_64/release/src/test.S.o.d" } } } ``` 我们可以通过遍历去获取处理每种类型的源文件。 ```lua for _, sourcebatch in pairs(target:sourcebatches()) do local sourcekind = sourcebatch.sourcekind if sourcekind == "cc" or sourcekind == "cxx" or sourcekind == "as" then for _, sourcefile in ipairs(sourcebatch.sourcefiles) do -- TODO end end end ``` 其中 sourcekind 是每种源文件的类型,cc 是 c 文件类型,cxx 是 c++ 源文件,as 是 asm 源文件。 sourcebatch 对应每种类型的源文件 batch,对应一批同类型源文件。 sourcebatch.sourcefiles 是源文件列表,sourcebatch.objectfiles 是对象文件列表,sourcebatch.rulename 是对应的规则名。 ## target:objectfiles * 获取所有对象文件列表 #### 函数原型 ::: tip API ```lua target:objectfiles() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 尽管 `target:sourcebatches()` 也可以获取所有对象文件,但是它们是根据源文件类型分类过的,且不直接参与最终链接。 如果我们想动态修改最终链接的对象文件列表,可以修改 `target:objectfiles()`,它是一个数组列表。 ## target:headerfiles * 获取所有的头文件列表 #### 函数原型 ::: tip API ```lua target:headerfiles() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 可以获取到 `add_headerfiles()` 接口设置的所有头文件列表。 ```lua for _, headerfile in ipairs(target:headerfiles()) do -- TODO end ``` ## target:scriptdir * 获取目标定义所在的 xmake.lua 目录 #### 函数原型 ::: tip API ```lua target:scriptdir() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 这通常在自定义规则中使用的比较多,想获取当前 target 实际被定义在哪个 xmake.lua 所在目录下,方便引用一些资源文件,可以用这个接口。 ## target:has\_cfuncs * 检测目标编译配置能否获取给定的 C 函数 #### 函数原型 ::: tip API ```lua target:has_cfuncs() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 这应该在 `on_config` 中使用,比如可以用它来判断当前目标能否获取到 zlib 依赖包的一些函数接口,然后自动定义 `HAVE_INFLATE`: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function (target) if target:has_cfuncs("inflate", {includes = "zlib.h"}) then target:add("defines", "HAVE_INFLATE") end end) ``` 尽管 option 也提供了类似的检测功能,但 option 的检测使用的是全局的平台工具链,它无法附带上 target 相关的一些编译配置, 也无法根据 target 设置不同编译工具链来适配检测,并且无法检测包里面的一些接口。 如果我们仅仅是想粗粒度的检测函数接口,并且 target 没有额外设置不同的工具链,那么 option 提供的检测功能已经足够使用了。 如果想要更细粒度控制检测,可以使用 target 实例接口提供的检测特性。 ## target:has\_cxxfuncs * 检测目标编译配置能否获取给定的 C++ 函数 #### 函数原型 ::: tip API ```lua target:has_cxxfuncs(funcs: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | funcs | 函数名或函数名列表 | #### 用法说明 用法跟 [target:has\_cfuncs](#target-has_cfuncs) 类似,只是这里主要用于检测 C++ 的函数。 不过,在检测函数的同时,我们还可以额外配置 std languages,来辅助检测。 ``` target:has_cxxfuncs("foo", {includes = "foo.h", configs = {languages = "cxx17"}}) ``` ## target:has\_ctypes * 检测目标编译配置能否获取给定的 C 类型 #### 函数原型 ::: tip API ```lua target:has_ctypes(types: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 类型名或类型名列表 | #### 用法说明 这应该在 `on_config` 中使用,如下所示: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function (target) if target:has_ctypes("z_stream", {includes = "zlib.h"}) then target:add("defines", "HAVE_ZSTEAM_T") end end) ``` ## target:has\_cxxtypes * 检测目标编译配置能否获取给定的 C++ 类型 #### 函数原型 ::: tip API ```lua target:has_cxxtypes(types: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 类型名或类型名列表 | #### 用法说明 用法跟 [target:has\_ctypes](#target-has_ctypes) 类似,只是这里主要用于检测 C++ 的类型。 ## target:has\_cflags * 检测目标编译配置能否获取给定的 C 编译 flags #### 函数原型 ::: tip API ```lua target:has_cflags(flags: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | flags | 编译标志或标志列表 | #### 用法说明 ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) if target:has_cxxflags("-fPIC") then target:add("defines", "HAS_PIC") end end) ``` ## target:has\_cxxflags * 检测目标编译配置能否获取给定的 C++ 编译 flags #### 函数原型 ::: tip API ```lua target:has_cxxflags(flags: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | flags | 编译标志或标志列表 | #### 用法说明 用法跟 [target:has\_cflags](#target-has_cflags) 类似,只是这里主要用于检测 C++ 的编译 flags。 ## target:has\_cincludes * 检测目标编译配置能否获取给定的 C 头文件 #### 函数原型 ::: tip API ```lua target:has_cincludes(includes: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 头文件或头文件列表 | #### 用法说明 这应该在 `on_config` 中使用,比如可以用它来判断当前目标能否获取到 zlib 依赖包的 zlib.h 头文件,然后自动定义 `HAVE_INFLATE`: ```lua add_requires("zlib") target("test") set_kind("binary") add_files("src/*.c") add_packages("zlib") on_config(function (target) if target:has_cincludes("zlib.h") then target:add("defines", "HAVE_ZLIB_H") end end) ``` ## target:has\_cxxincludes * 检测目标编译配置能否获取给定的 C++ 头文件 #### 函数原型 ::: tip API ```lua target:has_cxxincludes(includes: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 头文件或头文件列表 | #### 用法说明 用法跟 [target:has\_cincludes](#target-has_cincludes) 类似,只是这里主要用于检测 C++ 的头文件。 ## target:check\_csnippets * 检测是否可以编译和链接给定的 C 代码片段 #### 函数原型 ::: tip API ```lua target:check_csnippets(snippets: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | snippets | 代码片段或代码片段列表 | #### 用法说明 用法跟 [target:check\_cxxsnippets](#target-check_cxxsnippets) 类似,只是这里主要用于检测 C 的代码片段。 ## target:check\_cxxsnippets * 检测是否可以编译和链接给定的 C++ 代码片段 #### 函数原型 ::: tip API ```lua target:check_cxxsnippets(snippets: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | snippets | 代码片段或代码片段列表 | #### 用法说明 这应该在 `on_config` 中使用,如下所示: ```lua add_requires("libtins") target("test") set_kind("binary") add_files("src/*.cpp") add_packages("libtins") on_config(function (target) local has_snippet = target:check_cxxsnippets({test = [[ #include using namespace Tins; void test() { std::string name = NetworkInterface::default_interface().name(); printf("%s\n", name.c_str()); } ]]}, {configs = {languages = "c++11"}, includes = {"tins/tins.h"}})) if has_snippet then target:add("defines", "HAS_XXX") end end) ``` 默认仅仅检测编译链接是否通过,如果想要尝试运行时检测,可以再设置 `tryrun = true`。 ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) local has_int_4 = target:check_cxxsnippets({test = [[ return (sizeof(int) == 4)? 0 : -1; ]]}, {configs = {languages = "c++11"}, tryrun = true})) if has_int_4 then target:add("defines", "HAS_INT4") end end) ``` 我们也可以继续通过设置 `output = true` 来捕获检测的运行输出,并且加上自定义的 `main` 入口,实现完整的测试代码,而不仅仅是代码片段。 ```lua target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) local int_size = target:check_cxxsnippets({test = [[ #include int main(int argc, char** argv) { printf("%d", sizeof(int)); return 0; return 0; } ]]}, {configs = {languages = "c++11"}, tryrun = true, output = true})) end) ``` ## target:check\_sizeof * 检测类型大小 #### 函数原型 ::: tip API ```lua target:check_sizeof(types: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 类型名或类型名列表 | #### 用法说明 ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) print("sizeof(long) = %s", target:check_sizeof("long")) print("sizeof(string) = %s", target:check_sizeof("std::string", {includes = "string"})) if target:check_size("long") == 8 then target:add("defines", "LONG64") end end) ``` ```sh $ xmake sizeof(long) = 8 sizeof(string) = 24 ``` ## target:has\_features * 检测是否指定的 C/C++ 编译特性 #### 函数原型 ::: tip API ```lua target:has_features(features: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | features | 特性名或特性名列表 | #### 用法说明 它相比使用 `check_cxxsnippets` 来检测,会更加快一些,因为它仅仅执行一次预处理就能检测所有的编译器特性,而不是每次都去调用编译器尝试编译。 ``` target("test") set_kind("binary") add_files("src/*.cpp") on_config(function (target) if target:has_features("c_static_assert") then target:add("defines", "HAS_STATIC_ASSERT") end if target:has_features("cxx_constexpr") then target:add("defines", "HAS_CXX_CONSTEXPR") end end) ``` --- --- url: /zh/guide/introduction.md --- # 简介 {#introduction} ## 什么是 Xmake? {#what-is-xmake} xmake 是一个基于 Lua 的轻量级跨平台构建工具,使用 xmake.lua 维护项目构建,相比 makefile/CMakeLists.txt,配置语法更加简洁直观,对新手非常友好,短时间内就能快速入门,能够让用户把更多的精力集中在实际的项目开发上。 虽然,简单易用是 xmake 的一大特色,但 xmake 的功能也是非常强大的,既能够像 Make/Ninja 那样可以直接编译项目,也可以像 CMake/Meson 那样生成工程文件,还有内置的包管理系统来帮助用户解决 C/C++ 依赖库的集成使用问题。 目前,xmake 主要用于 C/C++ 项目的构建,但是同时也支持其他 native 语言的构建,可以实现跟 C/C++ 混合编译,同时编译速度也是非常的快,可以跟 Ninja 持平。 如果你想要了解更多,请参考:[在线文档](/zh/guide/quick-start), [Github](https://github.com/xmake-io/xmake) 以及 [Gitee](https://gitee.com/tboox/xmake),同时也欢迎加入我们的 [社区](/zh/about/contact)。 ![](/assets/img/index/xmake-basic-render.gif) --- --- url: /zh/guide/extras/build-cache.md --- # 编译缓存加速 {#build-cache-acceleration} ## 本地编译缓存 {#local-build-cache} 默认情况下,Xmake 会开启本地缓存。2.6.5 之前的版本默认使用外置的 ccache,而 2.6.6 之后版本,Xmake 提供了内置的跨平台本地缓存方案。 相比 ccache 等第三方独立进程,xmake 内部状态维护,更加便于优化,也避免了频繁的独立进程加载耗时,也避免了与守护进程额外的通信。 另外,内置的缓存能够更好地支持跨平台,Windows 上 msvc 也能够很好地支持,而 ccache 仅仅支持 gcc/clang。 当然,我们也可以通过下面的命令禁用缓存: ```sh $ xmake f --ccache=n ``` 注意:无论是否使用内置本地缓存,配置名都是 `--ccache=`,意思是 c/c++ 构建缓存,而不仅仅是指 ccache 工具的名字。 我们如果想继续使用外置的其他缓存工具,也可以通过下面的方式来配置。 ```sh $ xmake f --ccache=n --cxx="ccache gcc" --cc="ccache gcc" $ xmake ``` ## 远程编译缓存 {#remote-build-cache} 除了本地缓存,我们也提供了远程缓存服务,类似 mozilla 的 sscache,如果只是个人开发,平常不会用到它。 但是,如果是公司内部多人协同开发一个大型项目,仅靠分布式编译和本地缓存是不够的。我们还需要将编译的对象文件缓存到独立的服务器上进行共享。 这样,其他人即使首次编译,也不需要每次都去分布式编译它,直接从远程拉取缓存来加速编译。 另外,Xmake 提供的远程缓存服务,也是全平台支持的,不仅支持 gcc/clang,还支持 msvc。 ### 开启服务 {#start-service} 我们可以指定 `--ccache` 参数来开启远程编译缓存服务。当然如果不指定这个参数,xmake 会默认开启所有服务端配置的服务。 ```sh $ xmake service --ccache : listening 0.0.0.0:9092 .. ``` 我们也可以在开启服务的同时,回显详细日志信息。 ```sh $ xmake service --ccache -vD : listening 0.0.0.0:9092 .. ``` ### 以 Daemon 模式开启服务 ```sh $ xmake service --ccache --start $ xmake service --ccache --restart $ xmake service --ccache --stop ``` ### 配置服务端 {#configure-server} 我们可以运行 `xmake service` 命令,它会自动生成一个默认的 `server.conf` 配置文件,存储到 `~/.xmake/service/server.conf`。 ```sh $ xmake service generating the config file to /Users/ruki/.xmake/service/server.conf .. an token(590234653af52e91b9e438ed860f1a2b) is generated, we can use this token to connect service. generating the config file to /Users/ruki/.xmake/service/client.conf .. : listening 0.0.0.0:9692 .. ``` 然后,我们编辑它,修正服务器的监听端口(可选)。 ```sh $ cat ~/.xmake/service/server.conf { distcc_build = { listen = "0.0.0.0:9692", workdir = "/Users/ruki/.xmake/service/server/remote_cache" }, known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", tokens = { "590234653af52e91b9e438ed860f1a2b" } } ``` ### 配置客户端 {#configure-client} 客户端配置文件在 `~/.xmake/service/client.conf`,我们可以在里面配置客户端需要连接的服务器地址。 我们可以在 hosts 列表中配置多个服务器地址,以及对应的 token。 ```sh $cat ~/.xmake/service/client.conf { remote_cache = { connect = "127.0.0.1:9692, token = "590234653af52e91b9e438ed860f1a2b" } } } ``` #### 配置超时 {#configure-timeout} 默认情况下,客户端连接、收发数据都是无限等待不超时的,但是如果访问服务端的网络不稳定,有可能会导致访问卡死,这时可以配置超时来解决。 如果发生超时异常,就会自动退化到本地缓存,不会永远卡死。 我们可以配置 `send_timeout`、`recv_timeout` 和 `connect_timeout` 三种超时,如果在根节点设置,那么所有客户端服务都会生效。 ```sh $ cat ~/.xmake/service/client.conf { send_timeout = 5000, recv_timeout = 5000, connect_timeout = 5000 } ``` 我们也可以仅针对当前远程缓存服务配置超时,其他服务还是默认超时。 ```sh $ cat ~/.xmake/service/client.conf { distcc_build = { send_timeout = 5000, recv_timeout = 5000, connect_timeout = 5000, } } ``` ::: tip 注意 服务端配置同样支持超时配置。 ::: ### 用户认证和授权 {#user-authorization} 关于用户认证和授权,可以参考 [远程编译/用户认证和授权](/zh/guide/extras/remote-compilation#user-authorization) 里面的详细说明,用法是完全一致的。 ### 连接服务器 {#connect-server} 配置完认证和服务器地址后,就可以输入下面的命令,将当前工程连接到配置的服务器上。 我们需要在连接时,输入 `--ccache`,指定仅连接远程编译缓存服务。 ```sh $ cd projectdir $ xmake service --connect --ccache : connect 127.0.0.1:9692 .. : 127.0.0.1:9692 connected! ``` 我们也可以同时连接多个服务,比如分布式编译和远程编译缓存服务。 ```sh $ xmake service --connect --distcc --ccache ``` ::: tip 注意 如果不带任何参数,默认连接的是远程编译服务。 ::: ### 断开连接 {#disconnect} ```sh $ xmake service --disconnect --ccache ``` ### 清理服务器缓存 {#clean-server-cache} 我们也可以通过下面的命令,清理当前工程对应的远程服务器上的缓存。 ```sh $ xmake service --clean --ccache ``` 而如果我们执行 `xmake clean --all`,在连接了远程服务的状态下,也会自动清理所有的缓存。 ### 一些内部优化 {#optimizations} 1. 拉取远程缓存的快照,通过 bloom filter + lz4 回传本地后,用于快速判断缓存是否存在,避免频繁查询服务端缓存信息。 2. 配合本地缓存,可以避免频繁地请求远程服务器,拉取缓存。 3. 内部状态维护,相比 sscache 等独立工具,避免了频繁的独立进程加载耗时,也避免了与守护进程额外的通信。 --- --- url: /zh/guide/basic-commands/build-configuration.md --- # 编译配置 {#build-configuration} 通过`xmake f|config`配置命令,设置构建前的相关配置信息,详细参数选项,请运行: `xmake f --help`。 ::: tip 注意 你可以使用命令行缩写来简化输入,也可以使用全名,例如: `xmake f` 或者 `xmake config`. `xmake f -p linux` 或者 `xmake config --plat=linux`. ::: ## 切换平台 {#switch-platforms} ### 主机平台 ```sh $ xmake ``` ::: tip 注意 Xmake 将会自动探测当前主机平台,默认自动生成对应的目标程序。 ::: ### Linux ```sh $ xmake f -p linux [-a i386|x86_64] $ xmake ``` ### Android ```sh $ xmake f -p android --ndk=~/files/android-ndk-r10e/ [-a armeabi-v7a|arm64-v8a] $ xmake ``` 如果要手动指定 ndk 中具体某个工具链,而不是使用默认检测的配置,可以通过 [--bin](#-bin) 来设置,例如: ```sh $ 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) 的行为是一致的。 ::: tip 注意 如果手动设置了 bin 目录,没有通过检测,可以看下是否 `--arch=` 参数没有匹配对。 ::: ### iPhoneOS ```sh $ xmake f -p iphoneos [-a armv7|armv7s|arm64|i386|x86_64] $ xmake ``` 由于 m1 设备上模拟器也支持 arm64 架构,因此之前单纯从 arch 去区分是否为模拟器,已无法满足需求。 因此,2.6.5 版本,我们新增了一个参数配置去区分是否为模拟器目标。 ```sh $ xmake f -p iphoneos --appledev=simulator $ xmake f -p watchos --appledev=simulator $ xmake f -p appletvos --appledev=simulator ``` ### Mac Catalyst 我们也可以指定构建 Mac Catalyst 程序。 ```sh $ xmake f --appledev=catalyst ``` ### Windows ```sh $ xmake f -p windows [-a x86|x64] $ xmake ``` ### Mingw xmake 除了支持 Msys2/MingW, MingW for macOS/linux 之外,还支持 llvm-mingw 工具链,可以切换 arm/arm64 架构来编译。 ```sh $ xmake f -p mingw --sdk=/usr/local/i386-mingw32-4.3.0/ [-a i386|x86_64|arm|arm64] $ xmake ``` ### Apple WatchOS ```sh $ xmake f -p watchos [-a i386|armv7k] $ xmake ``` ### Wasm (WebAssembly) 此平台用于编译 WebAssembly 程序(内部会使用emcc工具链),在切换此平台之前,我们需要先进入 Emscripten 工具链环境,确保 emcc 等编译器可用。 ```sh $ xmake f -p wasm $ xmake ``` xmake 也支持 Qt for wasm 编译,只需要: ```sh $ xmake f -p wasm [--qt=~/Qt] $ xmake ``` 其中 `--qt` 参数设置是可选的,通常 xmake 都能检测到 qt 的 sdk 路径。 需要注意的一点是,Emscripten 和 Qt SDK 的版本是有对应关系的,不匹配的版本,可能会有 Qt/Wasm 之间的兼容问题。 关于版本对应关系,可以看下: 更多详情见: 除了 emscripten 以外,还有一个常用的 wasm 工具链 wasi-sdk,用于构建基于 wasi 的程序,我们仅仅只需要切换工具链即可。 ```sh $ xmake f -p wasm --toolchain=wasi $ xmake ``` ### HarmonyOS (鸿蒙) 2.9.1 版本新增了鸿蒙 OS 平台的 native 工具链编译支持: ```sh $ xmake f -p harmony ``` xmake 会自动探测默认的 SDK 路径,当然我们也可以指定 Harmony SDK 路径。 ```sh $ xmake f -p Harmony --sdk=/Users/ruki/Library/Huawei/Sdk/openharmony/10/native ``` ## 全局配置 我们也可以将一些常用配置保存到全局配置中,来简化频繁地输入: 例如: ```sh $ xmake g --ndk=~/files/android-ndk-r10e/ ``` 现在,我们重新配置和编译`android`程序: ```sh $ xmake f -p android $ xmake ``` 以后,就不需要每次重复配置 `--ndk=` 参数了。 ::: tip 注意 每个命令都有其简写,例如: `xmake g` 或者 `xmake global`. ::: ## 清除配置 有时候,配置出了问题编译不过,或者需要重新检测各种依赖库和接口,可以加上 `-c` 参数,清除缓存的配置,强制重新检测和配置 ```sh $ xmake f -c $ xmake ``` 或者: ```sh $ xmake f -p iphoneos -c $ xmake ``` ## 导入导出配置 2.5.5 之后,我们还可以导入导出已经配置好的配置集,方便配置的快速迁移。 ### 导出配置 ```sh $ xmake f --export=/tmp/config.txt $ xmake f -m debug --xxx=y --export=/tmp/config.txt ``` ### 导入配置 ```sh $ xmake f --import=/tmp/config.txt $ xmake f -m debug --xxx=y --import=/tmp/config.txt ``` ### 导出配置(带菜单) ```sh $ xmake f --menu --export=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --export=/tmp/config.txt ``` ### 导入配置(带菜单) ```sh $ xmake f --menu --import=/tmp/config.txt $ xmake f --menu -m debug --xxx=y --import=/tmp/config.txt ``` --- --- url: /zh/guide/package-management/network-optimization.md --- # 网络优化 {#network-optimization} 如果由于网络不稳定,导致下载包速度很慢或者下载失败,我们可以通过下面的一些方式来解决。 ## 手动下载 {#manual-download} 默认 xmake 会调用 curl、wget 等工具来下载,用户也可以手动用自己的下载器下载(也可以使用代理),把下载后的包放到自己的目录下,比如:`/download/packages/zlib-v1.0.tar.gz` 然后使用下面的命令,设置包下载的搜索目录: ```sh $ xmake g --pkg_searchdirs="/download/packages" ``` 然后重新执行 xmake 编译时,xmake 会优先从 `/download/packages` 找寻源码包,然后直接使用,不再自己下载了。 至于查找的包名是怎样的呢,可以通过下面的命令查看: ```sh $ xmake require --info zlib -> searchdirs: /download/packages -> searchnames: zlib-1.2.11.tar.gz ``` 我们可以看到对应的搜索目录以及搜索的包名。 ## 设置代理 {#proxy-setting} 如果觉得手动下载还是麻烦,我们也可以让 xmake 直接走代理。 ```sh $ xmake g --proxy="socks5://127.0.0.1:1086" $ xmake g --help -x PROXY, --proxy=PROXY Use proxy on given port. [PROTOCOL://]HOST[:PORT] e.g. - xmake g --proxy='http://host:port' - xmake g --proxy='https://host:port' - xmake g --proxy='socks5://host:port' ``` \--proxy 参数指定代理协议和地址,具体语法可以参考 curl 的,通常可以支持 http、https、socks5 等协议,但实际支持力度依赖 curl、wget 和 git,比如 wget 就不支持 socks5 协议。 我们可以通过下面的参数指定哪些 host 走代理,如果没设置,默认全局走代理。 ```sh --proxy_hosts=PROXY_HOSTS Only enable proxy for the given hosts list, it will enable all if be unset, and we can pass match pattern to list: e.g. - xmake g --proxy_hosts='github.com,gitlab.*,*.xmake.io' ``` 如果设置了 hosts 列表,那么之后这个列表里面匹配的 host 才走代理。 \--proxy\_host 支持多个 hosts 设置,逗号分隔,并且支持基础的模式匹配 \*.github.com,以及其他 lua 模式匹配规则也支持。 如果觉得上面的 hosts 模式配置还不够灵活,我们也可以走 pac 的自动代理配置规则: ```sh --proxy_pac=PROXY_PAC Set the auto proxy configuration file. (default: pac.lua) e.g. - xmake g --proxy_pac=pac.lua (in /Users/ruki/.xmake or absolute path) - function main(url, host) if host == 'github.com' then return true end end ``` ::: tip 注意 如果有proxy\_hosts优先走hosts配置,没有的话才走pac配置。 ::: pac 的默认路径:~/.xmake/pac.lua,如果 --proxy 被设置,并且这个文件存在,就会自动走 pac,如果不存在,也没 hosts,那就全局生效代理。 也可以手动指定 pac 全路径。 ```sh $ xmake g --proxy_pac=/xxxx/xxxxx_pac.lua ``` 配置规则描述: ```lua function main(url, host) if host:find("bintray.com") then return true end end ``` 如果返回 true,那么这个 url 和 host 就是走的代理,不返回或者返回 false,就是不走代理。 这块的具体详情见:https://github.com/xmake-io/xmake/issues/854 ::: tip 注意 另外,除了依赖包下载,其他涉及网络下载的命令也都支持代理,比如:`xmake update` ::: ## 镜像代理 {#mirror-proxy} v2.5.4 之后,pac.lua 配置里面还可以配置镜像代理规则,比如对所有 github.com 域名的访问切到 hub.fastgit.org 域名,实现加速下载包。 ```lua function mirror(url) return url:gsub("github.com", "hub.fastgit.org") end ``` ```sh $ xrepo install libpng > curl https://hub.fastgit.org/glennrp/libpng/archive/v1.6.37.zip -o v1.6.37.zip ``` --- --- url: /zh/examples/cpp/autogen.md --- # 自动代码生成 {#autogen} 在许多情况下,我们需要在编译之前对代码进行预处理和自动生成,然后将生成的代码参与到后续的编译流程中。 随着 Xmake 的不断迭代,对代码生成特性的支持也在持续更新和改进。目前主要支持以下几种方式: ## 最简单的方式 这种方式最为简单直接,只需在构建之前生成代码,并通过 `add_files` 强制添加进来。 由于 `add_files` 默认不会添加不存在的文件,所以需要配置 `always_added = true`,即使文件当前还不存在也能强制添加。 ```lua add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("$(builddir)/autogen.cpp", {always_added = true}) before_build(function (target) io.writefile("$(builddir)/autogen.cpp", [[ #include using namespace std; int main(int argc, char** argv) { cout << "hello world!" << endl; return 0; } ]]) end) ``` 这种方式有很多局限性,实际场景下不常用,但胜在简单易懂。 ## 依赖目标生成 有时我们需要通过执行项目中的某个目标程序来生成代码,可以通过如下方式实现: ```lua add_rules("mode.debug", "mode.release") rule("autogen") set_extensions(".in") before_buildcmd_file(function (target, batchcmds, sourcefile, opt) local sourcefile_cx = path.join(target:autogendir(), "rules", "autogen", path.basename(sourcefile) .. ".cpp") local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) batchcmds:show_progress(opt.progress, "${color.build.object}compiling.autogen %s", sourcefile) batchcmds:mkdir(path.directory(sourcefile_cx)) batchcmds:vrunv(target:dep("autogen"):targetfile(), {sourcefile, sourcefile_cx}) batchcmds:compile(sourcefile_cx, objectfile) batchcmds:add_depfiles(sourcefile, target:dep("autogen"):targetfile()) batchcmds:set_depmtime(os.mtime(objectfile)) batchcmds:set_depcache(target:dependfile(objectfile)) end) target("autogen") set_default(false) set_kind("binary") set_plat(os.host()) -- 生成当前主机平台程序 set_arch(os.arch()) add_files("src/autogen.cpp") set_languages("c++11") set_policy("build.fence", true) -- 禁用源码间并行编译 target("test") set_kind("binary") add_deps("autogen") add_rules("autogen") add_files("src/main.cpp") add_files("src/*.in") ``` 首先需要配置一个 autogen 目标程序,它必须能在当前编译平台运行,因此需通过 `set_plat(os.host())` 强制为主机平台编译。 另外,需要配置 `build.fence` 策略,禁用源码间并行编译,确保 autogen 目标源码优先编译,提前生成 autogen 可执行程序。 然后,配置自定义规则,在构建前调用 autogen 目标程序生成源码,并直接将生成的源码编译为对象文件,插入到后续链接流程中。 完整例子见:[autogen\_codedep](https://github.com/xmake-io/xmake/blob/dev/tests/projects/other/autogen/autogen_codedep/xmake.lua) ## 通过原生模块生成 Xmake 新增了原生模块开发特性,即使不定义额外的 autogen 目标程序,也可以通过原生模块生成代码。 关于原生模块开发,可参考文档:[Native 模块开发](/zh/api/scripts/native-modules)。 ```lua add_rules("mode.debug", "mode.release") add_moduledirs("modules") rule("autogen") set_extensions(".in") before_build_file(function (target, sourcefile, opt) import("utils.progress") import("core.project.depend") import("core.tool.compiler") import("autogen.foo", {always_build = true}) local sourcefile_cx = path.join(target:autogendir(), "rules", "autogen", path.basename(sourcefile) .. ".cpp") local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) depend.on_changed(function () progress.show(opt.progress, "${color.build.object}compiling.autogen %s", sourcefile) os.mkdir(path.directory(sourcefile_cx)) foo.generate(sourcefile, sourcefile_cx) compiler.compile(sourcefile_cx, objectfile, {target = target}) end, {dependfile = target:dependfile(objectfile), files = sourcefile, changed = target:is_rebuilt()}) end) target("test") set_kind("binary") add_rules("autogen") add_files("src/main.cpp") add_files("src/*.in") ``` 完整例子见:[Native 模块自动生成](https://github.com/xmake-io/xmake/blob/dev/tests/projects/other/autogen/autogen_shared_module/xmake.lua)。 ## Prepare 阶段生成 Xmake 3.x 之后,新增了 `on_prepare` 和 `on_prepare_files` 接口,实现两阶段编译。在 Prepare 阶段,可专门处理源码生成和预处理。 它会在所有 `on_build` 和 `on_build_files` 接口之前执行。 相关接口说明见文档:[Prepare 接口手册](/zh/api/description/project-target.html#on-prepare)。 --- --- url: /zh/guide/extras/autoscan-sourcecode.md --- # 自动源码扫描 {#autoscan-sourcecode} 对于一份工程源码,可以不用编写 makefile,也不用编写各种 make 相关的工程描述文件(例如:xmake.lua、makefile.am、CMakeLists.txt 等)。 xmake 就可以直接编译它们,这是如何做到的呢?简单来说下实现原理: 1. 首先扫描当前目录下,xmake 所支持的所有源码文件 2. 分析代码,检测哪些代码拥有 main 入口函数 3. 所有没有 main 入口的代码编译成静态库 4. 带有 main 入口的代码,编译成可执行程序,同时链接其他静态库程序 这种代码扫描和智能编译,非常简单,目前 xmake 还不支持多级目录扫描,只对单级目录的代码进行扫描编译。。 ## 使用场景 1. 临时快速编译和运行一些零散的测试代码 2. 尝试对其他开源库做移植编译 3. 快速基于现有代码创建新xmake工程 ## 如何使用 直接在带有源码的目录(没有xmake.lua)下执行xmake,然后根据提示操作: ```sh $ xmake note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)? please input: n (y/n) y ``` 另外,当存在其他构建系统标识性文件时(比如 CMakeLists.txt),不会触发自动生成 xmake.lua 的流程,而是首先触发自动探测构建系统并编译。 ```sh $ xmake f -y ``` ## 开源代码的移植和编译 虽然这种方式并不是非常智能,限制也不少,但是对于想临时写些代码进行编译运行,或者临时想交叉编译一些简单的开源库代码 这种方式已经足够使用了,下面看一个实际的例子: 我下载了一份 zlib-1.2.10 的源码,想要编译它,只需要进入 zlib 的源码目录执行: ```sh $ cd zlib-1.2.10 $ xmake note: xmake.lua not found, try generating it (pass -y or --confirm=y/n/d to skip confirm)? please input: n (y/n) y ``` 就行了,输入 y 确认后,输出结果如下: ``` target(zlib-1.2): static [+]: ./adler32.c [+]: ./compress.c [+]: ./crc32.c [+]: ./deflate.c [+]: ./gzclose.c [+]: ./gzlib.c [+]: ./gzread.c [+]: ./gzwrite.c [+]: ./infback.c [+]: ./inffast.c [+]: ./inflate.c [+]: ./inftrees.c [+]: ./trees.c [+]: ./uncompr.c [+]: ./zutil.c xmake.lua generated, scan ok!👌 checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.12 checking for the target minimal version ... 10.12 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the debugger (dd) ... xcrun -sdk macosx lldb checking for the golang compiler (go) ... go configure { ex = "xcrun -sdk macosx ar" , sh = "xcrun -sdk macosx clang++" , host = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , plat = "macosx" , xcode_dir = "/Applications/Xcode.app" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , go = "go" , target_minver = "10.12" , ccache = "ccache" , mode = "release" , clean = true , cxx = "xcrun -sdk macosx clang" , cc = "xcrun -sdk macosx clang" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.12" , sc = "xcrun -sdk macosx swiftc" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: cache compiling.release ./adler32.c [06%]: cache compiling.release ./compress.c [13%]: cache compiling.release ./crc32.c [20%]: cache compiling.release ./deflate.c [26%]: cache compiling.release ./gzclose.c [33%]: cache compiling.release ./gzlib.c [40%]: cache compiling.release ./gzread.c [46%]: cache compiling.release ./gzwrite.c [53%]: cache compiling.release ./infback.c [60%]: cache compiling.release ./inffast.c [66%]: cache compiling.release ./inflate.c [73%]: cache compiling.release ./inftrees.c [80%]: cache compiling.release ./trees.c [86%]: cache compiling.release ./uncompr.c [93%]: cache compiling.release ./zutil.c [100%]: archiving.release libzlib-1.2.a build ok!👌 ``` 通过输出结果,可以看到,xmake 会去检测扫描当前目录下的所有 .c 代码,发现没有 main 入口,应该是静态库程序,因此执行 xmake 后,就直接编译成静态库 libzlib-1.2.a 了。 这些 xmake.lua 都没有编写,其实 xmake 在扫描完成后,会自动在当前目录下生成一份 xmake.lua,下次编译就不需要重新扫描检测了。 自动生成的 xmake.lua 内容如下: ```lua -- define target target("zlib-1.2") -- set kind set_kind("static") -- add files add_files("./adler32.c") add_files("./compress.c") add_files("./crc32.c") add_files("./deflate.c") add_files("./gzclose.c") add_files("./gzlib.c") add_files("./gzread.c") add_files("./gzwrite.c") add_files("./infback.c") add_files("./inffast.c") add_files("./inflate.c") add_files("./inftrees.c") add_files("./trees.c") add_files("./uncompr.c") add_files("./zutil.c") ``` 也许你会说,像这种开源库,直接 `configure; make` 不就好了吗,他们自己也有提供 makefile 来直接编译的,的确是这样,我这里只是举个例子而已。。 当然,很多开源库在交叉编译的时候,通过自带的 `configure`,处理起来还是很繁琐的,用 xmake 进行交叉编译会更方便些。。 ## 即时地代码编写和编译运行 xmake 的这个扫描代码编译特性,主要的目的还是为了让我们在临时想写些测试代码的时候,不用考虑太多东西,直接上手敲代码,然后快速执行 `xmake run` 来调试验证结果。 例如: 我想写一个简单的 main.c 测试程序,打印 `hello world!`,如果要写 makefile 或者直接通过 gcc 命令来,就很繁琐了,你需要: ```sh gcc ./main.c -o demo ./demo ``` 最快的方式,也需要执行两行命令,而如果用 xmake,只需要执行: ```sh xmake run ``` 就行了,它会自动检测到代码后,自动生成对应的 xmake.lua,自动编译,自动运行,然后输出: ``` hello world! ``` 如果你有十几个代码文件,用手敲 gcc 的方式,或者写 makefile 的方式,这个差距就更明显了,用 xmake 还是只需要一行命令: ```sh xmake run ``` ## 多语言支持 这种代码检测和即时编译,是支持多语言的,不仅支持 c/c++,还支持 objc/swift,后期还会支持 golang(正在开发中) 例如我下载了一份 fmdb 的 ios 开源框架代码: ``` . ├── FMDB.h ├── FMDatabase.h ├── FMDatabase.m ├── FMDatabaseAdditions.h ├── FMDatabaseAdditions.m ├── FMDatabasePool.h ├── FMDatabasePool.m ├── FMDatabaseQueue.h ├── FMDatabaseQueue.m ├── FMResultSet.h └── FMResultSet.m ``` 想要把它编译成 ios 的静态库,但是又不想写 xmake.lua,或者 makefile,那么只需要使用 xmake 的这个新特性,直接执行: ```sh $ xmake f -p iphoneos; xmake ``` 就行了,输出结果如下: ``` xmake.lua not found, scanning files .. target(FMDB): static [+]: ./FMDatabase.m [+]: ./FMDatabaseAdditions.m [+]: ./FMDatabasePool.m [+]: ./FMDatabaseQueue.m [+]: ./FMResultSet.m xmake.lua generated, scan ok!👌 checking for the architecture ... armv7 checking for the Xcode SDK version for iphoneos ... 10.1 checking for the target minimal version ... 10.1 checking for the c compiler (cc) ... xcrun -sdk iphoneos clang checking for the c++ compiler (cxx) ... xcrun -sdk iphoneos clang checking for the objc compiler (mm) ... xcrun -sdk iphoneos clang checking for the objc++ compiler (mxx) ... xcrun -sdk iphoneos clang++ checking for the assember (as) ... gas-preprocessor.pl xcrun -sdk iphoneos clang checking for the linker (ld) ... xcrun -sdk iphoneos clang++ checking for the static library archiver (ar) ... xcrun -sdk iphoneos ar checking for the static library extractor (ex) ... xcrun -sdk iphoneos ar checking for the shared library linker (sh) ... xcrun -sdk iphoneos clang++ checking for the swift compiler (sc) ... xcrun -sdk iphoneos swiftc configure { ex = "xcrun -sdk iphoneos ar" , ccache = "ccache" , host = "macosx" , ar = "xcrun -sdk iphoneos ar" , buildir = "build" , as = "/usr/local/share/xmake/tools/utils/gas-preprocessor.pl xcrun -sdk iphoneos clang" , arch = "armv7" , mxx = "xcrun -sdk iphoneos clang++" , cxx = "xcrun -sdk iphoneos clang" , target_minver = "10.1" , xcode_dir = "/Applications/Xcode.app" , clean = true , sh = "xcrun -sdk iphoneos clang++" , cc = "xcrun -sdk iphoneos clang" , ld = "xcrun -sdk iphoneos clang++" , mode = "release" , kind = "static" , plat = "iphoneos" , xcode_sdkver = "10.1" , sc = "xcrun -sdk iphoneos swiftc" , mm = "xcrun -sdk iphoneos clang" } configure ok! clean ok! [00%]: cache compiling.release ./FMDatabase.m [20%]: cache compiling.release ./FMDatabaseAdditions.m [40%]: cache compiling.release ./FMDatabasePool.m [60%]: cache compiling.release ./FMDatabaseQueue.m [80%]: cache compiling.release ./FMResultSet.m [100%]: archiving.release libFMDB.a build ok!👌 ``` ## 同时编译多个可执行文件 输出结果的开头部分,就是对代码的分析结果,虽然目前只支持单级目录结构的代码扫描,但是还是可以同时支持检测和编译多个可执行文件的 我们以 libjpeg 的开源库为例: 我们进入 jpeg-6b 目录后,执行: ```sh $ xmake ``` 输出如下: ``` xmake.lua not found, scanning files .. target(jpeg-6b): static [+]: ./cdjpeg.c [+]: ./example.c [+]: ./jcapimin.c [+]: ./jcapistd.c [+]: ./jccoefct.c [+]: ./jccolor.c [+]: ./jcdctmgr.c [+]: ./jchuff.c [+]: ./jcinit.c [+]: ./jcmainct.c [+]: ./jcmarker.c [+]: ./jcmaster.c [+]: ./jcomapi.c [+]: ./jcparam.c [+]: ./jcphuff.c [+]: ./jcprepct.c [+]: ./jcsample.c [+]: ./jctrans.c [+]: ./jdapimin.c [+]: ./jdapistd.c [+]: ./jdatadst.c [+]: ./jdatasrc.c [+]: ./jdcoefct.c [+]: ./jdcolor.c [+]: ./jddctmgr.c [+]: ./jdhuff.c [+]: ./jdinput.c [+]: ./jdmainct.c [+]: ./jdmarker.c [+]: ./jdmaster.c [+]: ./jdmerge.c [+]: ./jdphuff.c [+]: ./jdpostct.c [+]: ./jdsample.c [+]: ./jdtrans.c [+]: ./jerror.c [+]: ./jfdctflt.c [+]: ./jfdctfst.c [+]: ./jfdctint.c [+]: ./jidctflt.c [+]: ./jidctfst.c [+]: ./jidctint.c [+]: ./jidctred.c [+]: ./jmemansi.c [+]: ./jmemmgr.c [+]: ./jmemname.c [+]: ./jmemnobs.c [+]: ./jquant1.c [+]: ./jquant2.c [+]: ./jutils.c [+]: ./rdbmp.c [+]: ./rdcolmap.c [+]: ./rdgif.c [+]: ./rdppm.c [+]: ./rdrle.c [+]: ./rdswitch.c [+]: ./rdtarga.c [+]: ./transupp.c [+]: ./wrbmp.c [+]: ./wrgif.c [+]: ./wrppm.c [+]: ./wrrle.c [+]: ./wrtarga.c target(ansi2knr): binary [+]: ./ansi2knr.c target(cjpeg): binary [+]: ./cjpeg.c target(ckconfig): binary [+]: ./ckconfig.c target(djpeg): binary [+]: ./djpeg.c target(jpegtran): binary [+]: ./jpegtran.c target(rdjpgcom): binary [+]: ./rdjpgcom.c target(wrjpgcom): binary [+]: ./wrjpgcom.c xmake.lua generated, scan ok!👌 checking for the architecture ... x86_64 checking for the Xcode SDK version for macosx ... 10.12 checking for the target minimal version ... 10.12 checking for the c compiler (cc) ... xcrun -sdk macosx clang checking for the c++ compiler (cxx) ... xcrun -sdk macosx clang checking for the objc compiler (mm) ... xcrun -sdk macosx clang checking for the objc++ compiler (mxx) ... xcrun -sdk macosx clang++ checking for the swift compiler (sc) ... xcrun -sdk macosx swiftc checking for the assember (as) ... xcrun -sdk macosx clang checking for the linker (ld) ... xcrun -sdk macosx clang++ checking for the static library archiver (ar) ... xcrun -sdk macosx ar checking for the static library extractor (ex) ... xcrun -sdk macosx ar checking for the shared library linker (sh) ... xcrun -sdk macosx clang++ checking for the debugger (dd) ... xcrun -sdk macosx lldb checking for the golang compiler (go) ... go configure { ex = "xcrun -sdk macosx ar" , sh = "xcrun -sdk macosx clang++" , host = "macosx" , ar = "xcrun -sdk macosx ar" , buildir = "build" , as = "xcrun -sdk macosx clang" , plat = "macosx" , xcode_dir = "/Applications/Xcode.app" , arch = "x86_64" , mxx = "xcrun -sdk macosx clang++" , go = "go" , target_minver = "10.12" , ccache = "ccache" , mode = "release" , clean = true , cxx = "xcrun -sdk macosx clang" , cc = "xcrun -sdk macosx clang" , dd = "xcrun -sdk macosx lldb" , kind = "static" , ld = "xcrun -sdk macosx clang++" , xcode_sdkver = "10.12" , sc = "xcrun -sdk macosx swiftc" , mm = "xcrun -sdk macosx clang" } configure ok! clean ok! [00%]: cache compiling.release ./cdjpeg.c [00%]: cache compiling.release ./example.c [00%]: cache compiling.release ./jcapimin.c [00%]: cache compiling.release ./jcapistd.c [00%]: cache compiling.release ./jccoefct.c [00%]: cache compiling.release ./jccolor.c [01%]: cache compiling.release ./jcdctmgr.c [01%]: cache compiling.release ./jchuff.c [01%]: cache compiling.release ./jcinit.c [01%]: cache compiling.release ./jcmainct.c [01%]: cache compiling.release ./jcmarker.c [02%]: cache compiling.release ./jcmaster.c [02%]: cache compiling.release ./jcomapi.c [02%]: cache compiling.release ./jcparam.c [02%]: cache compiling.release ./jcphuff.c [02%]: cache compiling.release ./jcprepct.c [03%]: cache compiling.release ./jcsample.c [03%]: cache compiling.release ./jctrans.c [03%]: cache compiling.release ./jdapimin.c [03%]: cache compiling.release ./jdapistd.c [03%]: cache compiling.release ./jdatadst.c [04%]: cache compiling.release ./jdatasrc.c [04%]: cache compiling.release ./jdcoefct.c [04%]: cache compiling.release ./jdcolor.c [04%]: cache compiling.release ./jddctmgr.c [04%]: cache compiling.release ./jdhuff.c [05%]: cache compiling.release ./jdinput.c [05%]: cache compiling.release ./jdmainct.c [05%]: cache compiling.release ./jdmarker.c [05%]: cache compiling.release ./jdmaster.c [05%]: cache compiling.release ./jdmerge.c [06%]: cache compiling.release ./jdphuff.c [06%]: cache compiling.release ./jdpostct.c [06%]: cache compiling.release ./jdsample.c [06%]: cache compiling.release ./jdtrans.c [06%]: cache compiling.release ./jerror.c [07%]: cache compiling.release ./jfdctflt.c [07%]: cache compiling.release ./jfdctfst.c [07%]: cache compiling.release ./jfdctint.c [07%]: cache compiling.release ./jidctflt.c [07%]: cache compiling.release ./jidctfst.c [08%]: cache compiling.release ./jidctint.c [08%]: cache compiling.release ./jidctred.c [08%]: cache compiling.release ./jmemansi.c [08%]: cache compiling.release ./jmemmgr.c [08%]: cache compiling.release ./jmemname.c [09%]: cache compiling.release ./jmemnobs.c [09%]: cache compiling.release ./jquant1.c [09%]: cache compiling.release ./jquant2.c [09%]: cache compiling.release ./jutils.c [09%]: cache compiling.release ./rdbmp.c [10%]: cache compiling.release ./rdcolmap.c [10%]: cache compiling.release ./rdgif.c [10%]: cache compiling.release ./rdppm.c [10%]: cache compiling.release ./rdrle.c [10%]: cache compiling.release ./rdswitch.c [11%]: cache compiling.release ./rdtarga.c [11%]: cache compiling.release ./transupp.c [11%]: cache compiling.release ./wrbmp.c [11%]: cache compiling.release ./wrgif.c [11%]: cache compiling.release ./wrppm.c [12%]: cache compiling.release ./wrrle.c [12%]: cache compiling.release ./wrtarga.c [12%]: archiving.release libjpeg-6b.a [12%]: cache compiling.release ./wrjpgcom.c [25%]: linking.release wrjpgcom [25%]: cache compiling.release ./ansi2knr.c [37%]: linking.release ansi2knr [37%]: cache compiling.release ./jpegtran.c [50%]: linking.release jpegtran [50%]: cache compiling.release ./djpeg.c [62%]: linking.release djpeg [62%]: cache compiling.release ./ckconfig.c [75%]: linking.release ckconfig [75%]: cache compiling.release ./rdjpgcom.c [87%]: linking.release rdjpgcom [87%]: cache compiling.release ./cjpeg.c [100%]: linking.release cjpeg build ok!👌 ``` 可以看到,处理静态库,xmake 还分析出了很多可执行的测试程序,剩下的代码统一编译成一个 libjpeg.a 的静态库,供哪些测试程序链接使用。。 ``` target(ansi2knr): binary [+]: ./ansi2knr.c target(cjpeg): binary [+]: ./cjpeg.c target(ckconfig): binary [+]: ./ckconfig.c target(djpeg): binary [+]: ./djpeg.c target(jpegtran): binary [+]: ./jpegtran.c target(rdjpgcom): binary [+]: ./rdjpgcom.c target(wrjpgcom): binary [+]: ./wrjpgcom.c ``` ## 遇到的一些问题和限制 当前 xmake 的这种自动分析检测还不是非常智能,对于: 1. 需要特殊的编译选项 2. 需要依赖其他目录的头文件搜索 3. 需要分条件编译不同源文件 4. 同目录需要生成多个静态库 5. 需要多级目录支持的源码库 以上这些情况,xmake 暂时还没发自动化的智能处理,其中限制 1,2 还是可以解决的,通过半手动的方式,例如: ```sh $ xmake f --cxflags="" --ldflags="" --includedirs="" --linkdirs=""; xmake ``` 在自动检测编译的时候,手动配置这个源码工程需要的特殊编译选项,就可以直接通过编译了 而限制 3,暂时只能通过删源代码来解决了,就像刚才编译 jpeg 的代码,其实它的目录下面同时存在了: ``` ``` --- --- url: /zh/api/description/custom-toolchain.md --- # 自定义工具链 {#custom-toolchain} 在2.3.4版本之后,xmake已经支持在用户的项目xmake.lua中自定义工具链,例如: ```lua -- define toolchain toolchain("myclang") -- mark as standalone toolchain set_kind("standalone") -- set toolset set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") add_defines("MYCLANG") -- check toolchain on_check(function (toolchain) return import("lib.detect.find_tool")("clang") end) -- on load on_load(function (toolchain) -- get march local march = is_arch("x86_64", "x64") and "-m64" or "-m32" -- init flags for c/c++ toolchain:add("cxflags", march) toolchain:add("ldflags", march) toolchain:add("shflags", march) if not is_plat("windows") and os.isdir("/usr") then for _, includedir in ipairs({"/usr/local/include", "/usr/include"}) do if os.isdir(includedir) then toolchain:add("includedirs", includedir) end end for _, linkdir in ipairs({"/usr/local/lib", "/usr/lib"}) do if os.isdir(linkdir) then toolchain:add("linkdirs", linkdir) end end end -- init flags for objc/c++ (with ldflags and shflags) toolchain:add("mxflags", march) -- init flags for asm toolchain:add("asflags", march) end) ``` 然后通过下面的命令切到自己定义的工具链就行了: ```sh $ xmake f --toolchain=myclang ``` 当然,我们也可以通过`set_toolchains`接口直接对指定target切换设置到自定义工具链。 在自定义工具前,我们可以通过先运行以下命令,查看完整的内置工具链列表,确保xmake没有提供,如果有的话,直接使用就行了,没必要自己定义: ```sh $ xmake show -l toolchains ``` ## toolchain * 定义工具链 #### 函数原型 ::: tip API ```lua toolchain(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 工具链名称字符串 | #### 用法说明 可以在用户项目xmake.lua中定义,也可以通过includes独立到单独的xmake.lua去专门定义各种工具链 ```lua toolchain("myclang") set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") toolchain_end() ``` * 定义交叉工具链 我们也可以在 xmake.lua 中针对不同的交叉工具链sdk进行自定义配置,通常只需要指定 sdkdir,xmake就可以自动检测其他的配置,比如 cross 等信息,例如: ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") toolchain_end() target("hello") set_kind("binary") add_files("apps/hello/*.c") ``` 这是一个最精简的交叉工具链配置,仅仅设置了对应的sdk路径,然后通过 `set_kind("standalone")` 将其标记为完整独立的工具链。 这个时候,我们就可以通过命令行 `--toolchain=my_toolchain` 去手动切换到此工具链来使用。 ```sh xmake f --toolchain=my_toolchain xmake ``` 另外,我们还可以直接在 xmake.lua 中通过 `set_toolchains` 将其绑定到对应的 target 上去,那么仅仅只在编译此 target 时候,才会切换到我们自定义的工具链。 ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") toolchain_end() target("hello") set_kind("binary") add_files("apps/hello/*.c") set_toolchains("my_toolchain") ``` 这样,我们不再需要手动切换工具链了,只需要执行 xmake,就会默认自动切换到 my\_toolchain 工具链。 这对于嵌入式开发来讲尤其有用,因为嵌入式平台的交叉编译工具链非常多,我们经常需要各种切换来完成不同平台的编译。 因此,我们可以将所有的工具链定义放置到独立的 lua 文件中去定义,例如: ``` projectdir - xmake.lua - toolchains - my_toolchain1.lua - my_toolchain2.lua - ... ``` 然后,我们只需要再 xmake.lua 中通过 includes 去引入它们,并根据不同的自定义平台,绑定不同的工具链: ```lua includes("toolchains/*.lua") target("hello") set_kind("binary") add_files("apps/hello/*.c") if is_plat("myplat1") then set_toolchains("my_toolchain1") elseif is_plat("myplat2") then set_toolchains("my_toolchain2") end ``` 这样,我们就可以编译的时候,直接快速切换指定平台,来自动切换对应的工具链了。 ```sh xmake f -p myplat1 xmake ``` 如果,有些交叉编译工具链结构复杂,自动检测还不足够,那么可以根据实际情况,使用 `set_toolset`, `set_cross` 和 `set_bindir` 等接口,针对性的配置上其他的设置。 例如下面的例子,我们还额外添加了一些 cxflags/ldflags 以及内置的系统库 links。 ```lua toolchain("my_toolchain") set_kind("standalone") set_sdkdir("/tmp/arm-linux-musleabi-cross") on_load(function (toolchain) -- add flags for arch if toolchain:is_arch("arm") then toolchain:add("cxflags", "-march=armv7-a", "-msoft-float", {force = true}) toolchain:add("ldflags", "-march=armv7-a", "-msoft-float", {force = true}) end toolchain:add("ldflags", "--static", {force = true}) toolchain:add("syslinks", "gcc", "c") end) ``` 更多自定义工具链的例子,我们可以看下面的接口文档,也可以到 xmake 的源码的目录参考内置的工具链定义:[内部工具链列表](https://github.com/xmake-io/xmake/blob/master/xmake/toolchains/) ## set\_kind * 设置工具链类型 #### 函数原型 ::: tip API ```lua set_kind(kind: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | kind | 工具链类型: "standalone" | #### 用法说明 目前仅支持设置为`standalone`类型,表示当前工具链是独立完整的工具链,包括cc/cxx/ld/sh/ar等编译器、归档器、链接器等一整套工具集的配置。 通常用于某个target被同时设置了多个工具链的情况,但同时只能生效一个独立工具链,通过此配置可以保证生效的工具链存在互斥关系,比如gcc/clang工具链不会同时生效。 而像yasm/nasm这种局部工具链,属于附加的局部工具链扩展,不用设置standalone,因为clang/yasm两个工具链有可能同时存在。 ::: tip 注意 只要记住,存在完整编译环境的工具链,都设置为standalone就行了 ::: ## set\_toolset * 设置工具集 #### 函数原型 ::: tip API ```lua set_toolset(tool: , tools: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | tool | 工具名称字符串 (cc, cxx, ld, sh, ar, ex, strip, mm, mxx, as) | | tools | 工具程序名称字符串或数组 | | ... | 可变参数,可传递多个工具名称 | #### 用法说明 用于设置每个单独工具名和路径,例如: ```lua toolchain("myclang") set_kind("standalone") set_toolset("cc", "clang") set_toolset("cxx", "clang", "clang++") set_toolset("ld", "clang++", "clang") set_toolset("sh", "clang++", "clang") set_toolset("ar", "ar") set_toolset("ex", "ar") set_toolset("strip", "strip") set_toolset("mm", "clang") set_toolset("mxx", "clang", "clang++") set_toolset("as", "clang") ``` 关于这个接口的详情,可以看下:[target.set\_toolset](/zh/api/description/project-target.html#set-toolset) ## set\_sdkdir * 设置工具链sdk目录路径 #### 函数原型 ::: tip API ```lua set_sdkdir(sdkdir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | sdkdir | SDK目录路径字符串 | #### 用法说明 通常我们可以通过`xmake f --toolchain=myclang --sdk=xxx` 来配置 sdk 目录,但是每次配置比较繁琐,我们也可以通过此接口预先配置到 xmake.lua 中去,方便快速切换使用。 ```lua toolchain("myclang") set_kind("standalone") set_sdkdir("/tmp/sdkdir") set_toolset("cc", "clang") ``` ## set\_bindir * 设置工具链bin目录路径 #### 函数原型 ::: tip API ```lua set_bindir(bindir: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | bindir | 二进制目录路径字符串 | #### 用法说明 通常我们可以通过`xmake f --toolchain=myclang --bin=xxx`来配置 sdk 目录,但是每次配置比较繁琐,我们也可以通过此接口预先配置到 xmake.lua 中去,方便快速切换使用。 ```lua toolchain("myclang") set_kind("standalone") set_bindir("/tmp/sdkdir/bin") set_toolset("cc", "clang") ``` ## on\_check * 检测工具链 #### 函数原型 ::: tip API ```lua on_check(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 检测脚本函数,参数为toolchain | #### 用法说明 用于检测指定工具链所在sdk或者程序在当前系统上是否存在,通常用于多个 standalone 工具链的情况,进行自动探测和选择有效工具链。 而对于 `xmake f --toolchain=myclang` 手动指定的场景,此检测配置不是必须的,可以省略。 ```lua toolchain("myclang") on_check(function (toolchain) return import("lib.detect.find_tool")("clang") end) ``` ## on\_load * 加载工具链 #### 函数原型 ::: tip API ```lua on_load(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 加载脚本函数,参数为toolchain | #### 用法说明 对于一些复杂的场景,我们可以在 on\_load 中动态灵活的设置各种工具链配置,比在描述域设置更加灵活,更加强大: ```lua toolchain("myclang") set_kind("standalone") on_load(function (toolchain) -- set toolset toolchain:set("toolset", "cc", "clang") toolchain:set("toolset", "ld", "clang++") -- init flags local march = toolchain:is_arch("x86_64", "x64") and "-m64" or "-m32" toolchain:add("cxflags", march) toolchain:add("ldflags", march) toolchain:add("shflags", march) end) ``` ## toolchain\_end * 结束定义工具链 #### 函数原型 ::: tip API ```lua toolchain_end() ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | - | 无参数 | #### 用法说明 这个是可选的,如果想要手动结束 toolchain 的定义,可以调用它: ```lua toolchain("myclang") -- .. toolchain_end() ``` --- --- url: /zh/api/description/custom-rule.md --- # 自定义规则 {#custom-rule} 2.2.1发布后,Xmake 不仅原生支持多语言文件的构建,还允许用户通过自定义构建规则实现复杂的未知文件构建。 自定义构建规则可以使用 `set_extensions` 将一组文件扩展名关联到它们。 一旦这些扩展与规则相关联,稍后对 `add_files` 的调用将自动使用此自定义规则。 这是一个示例规则,它将使用 Pandoc 将添加到构建目标的 Markdown 文件转换为 HTML 文件: ```lua -- Define a build rule for a markdown file rule("markdown") set_extensions(".md", ".markdown") on_build_file(function (target, sourcefile, opt) import("core.project.depend") import("utils.progress") -- it only for v2.5.9, we need use print to show progress below v2.5.8 -- make sure build directory exists os.mkdir(target:targetdir()) -- replace .md with .html local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- only rebuild the file if its changed since last run depend.on_changed(function () -- call pandoc to make a standalone html file from a markdown file os.vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) progress.show(opt.progress, "${color.build.object}markdown %s", sourcefile) end, {files = sourcefile}) end) target("test") set_kind("object") -- 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") ``` 还有一种以 `on_build_files` 形式代替 `on_build_file` 的方法,它允许您在一个函数调用中处理整个文件集。 第二种称为 `on_buildcmd_file` 和 `on_buildcmd_files` 的形式是声明性的;它不是运行任意 Lua 来构建目标,而是运行 Lua 来了解这些目标是如何构建的。 `buildcmd` 的优点是可以将这些规则导出到根本不需要 xmake 即可运行的 makefile。 我们可以使用 buildcmd 进一步简化它,如下所示: ```lua -- Define a build rule for a markdown file rule("markdown") set_extensions(".md", ".markdown") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) -- make sure build directory exists batchcmds:mkdir(target:targetdir()) -- replace .md with .html local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- call pandoc to make a standalone html file from a markdown file batchcmds:vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) batchcmds:show_progress(opt.progress, "${color.build.object}markdown %s", sourcefile) -- only rebuild the file if its changed since last run batchcmds:add_depfiles(sourcefile) end) target("test") set_kind("object") -- 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` 自定义属性来完成此操作,如下例所示: ```lua target("test") -- ... add_files("src/test/*.md.in", {rule = "markdown"}) ``` 一个target可以叠加应用多个rules去更加定制化实现自己的构建行为,甚至支持不同的构建环境。 ::: tip 注意 通过`add_files("*.md", {rule = "markdown"})`方式指定的规则,优先级高于`add_rules("markdown")`设置的规则。 ::: ## rule * 定义规则 #### 函数原型 ::: tip API ```lua rule(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 规则名称字符串 | #### 用法说明 ```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) ``` ## add\_deps * 添加规则依赖 #### 函数原型 ::: tip API ```lua add_deps(deps: , ..., { order = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | deps | 依赖规则名称字符串或数组 | | ... | 可变参数,可传递多个依赖名称 | | order | 是否按顺序执行依赖 | #### 用法说明 关联依赖可以绑定一批规则,也就是不必对 target 挨个去使用 `add_rules()` 添加规则,只需要应用一个规则,就能生效它和它的所有依赖规则。 例如: ```lua rule("foo") add_deps("bar") rule("bar") ... ``` 我们只需要 `add_rules("foo")`,就能同时应用 foo 和 bar 两个规则。 但是,默认情况下,依赖之间是不存在执行的先后顺序的,foo 和 bar 的 `on_build_file` 等脚本是并行执行的,顺序未定义。 如果要严格控制执行顺序,可以配置 `add_deps("bar", {order = true})`,告诉 xmake,我们需要根据依赖顺序来执行同级别的脚本。 例如: ```lua rule("foo") add_deps("bar", {order = true}) on_build_file(function (target, sourcefile) end) rule("bar") on_build_file(function (target, sourcefile) end) ``` bar 的 `on_build_file` 将会被先执行。 ::: tip 注意 控制依赖顺序,我们需要 xmake 2.7.2 以上版本才支持。 ::: 不过,这种控制依赖的方式,只适合 foo 和 bar 两个规则都是自定义规则,如果想要将自己的规则插入到 xmake 的内置规则之前执行,这就不适用了。 这个时候,我们需要使用更加灵活的动态规则创建和注入的方式,去修改内置规则。 例如,我们想在内置的 `c++.build` 规则之前,执行自定义 cppfront 规则的 `on_build_file` 脚本,我们可以通过下面的方式来实现。 ```lua rule("cppfront") set_extensions(".cpp2") on_load(function (target) local rule = target:rule("c++.build"):clone() rule:add("deps", "cppfront", {order = true}) target:rule_add(rule) end) on_build_file(function (target, sourcefile, opt) print("build cppfront file") end) target("test") set_kind("binary") add_rules("cppfront") add_files("src/*.cpp") add_files("src/*.cpp2") ``` ## add\_imports * 为所有自定义脚本预先导入扩展模块 #### 函数原型 ::: tip API ```lua add_imports(modules: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | modules | 模块名称字符串或数组 | | ... | 可变参数,可传递多个模块名称 | #### 用法说明 使用方式和说明请见:[target:add\_imports](/zh/api/description/project-target#add-imports),用法相同。 ## set\_extensions * 设置规则支持的文件扩展类型 #### 函数原型 ::: tip API ```lua set_extensions(extensions: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | 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") ``` ## on\_load * 自定义加载脚本 #### 函数原型 ::: tip API ```lua on_load(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 加载脚本函数,参数为target | #### 用法说明 用于实现自定规则的加载脚本,当加载target的时候,会被执行,可在里面自定义设置一些target配置,例如: ```lua rule("test") on_load(function (target) target:add("defines", "TEST") end) ``` ## on\_link * 自定义链接脚本 #### 函数原型 ::: tip API ```lua on_link(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 链接脚本函数,参数为target | #### 用法说明 用于实现自定规则的链接脚本,会覆盖被应用的target的默认链接行为,例如: ```lua rule("test") on_link(function (target) end) ``` ## on\_config * 自定义配置脚本 #### 函数原型 ::: tip API ```lua on_config(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 配置脚本函数,参数为target | #### 用法说明 在 `xmake config` 执行完成后,Build 之前会执行此脚本,通常用于编译前的配置工作。它与 on\_load 不同的是,on\_load 只要 target 被加载就会执行,执行时机更早。 如果一些配置,无法在 on\_load 中过早配置,那么都可以在 on\_config 中去配置它。 另外,它的执行时机比 before\_build 还要早,大概的执行流程如下: ``` on_load -> after_load -> on_config -> before_build -> on_build -> after_build ``` ## on\_build * 自定义编译脚本 #### 函数原型 ::: tip API ```lua on_build(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译脚本函数,参数为target | #### 用法说明 用于实现自定规则的构建脚本,会覆盖被应用的target的默认构建行为,例如: ```lua rule("markdown") on_build(function (target) end) ``` ## on\_clean * 自定义清理脚本 #### 函数原型 ::: tip API ```lua on_clean(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 清理脚本函数,参数为target | #### 用法说明 用于实现自定规则的清理脚本会,覆盖被应用的target的默认清理行为,例如: ```lua rule("markdown") on_clean(function (target) -- remove sourcefile.html end) ``` ## on\_package * 自定义打包脚本 #### 函数原型 ::: tip API ```lua on_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包脚本函数,参数为target | #### 用法说明 用于实现自定规则的打包脚本,覆盖被应用的target的默认打包行为, 例如: ```lua rule("markdown") on_package(function (target) -- package sourcefile.html end) ``` ## on\_install * 自定义安装脚本 #### 函数原型 ::: tip API ```lua on_install(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装脚本函数,参数为target | #### 用法说明 用于实现自定规则的安装脚本,覆盖被应用的target的默认安装行为, 例如: ```lua rule("markdown") on_install(function (target) end) ``` ## on\_uninstall * 自定义卸载脚本 #### 函数原型 ::: tip API ```lua on_uninstall(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载脚本函数,参数为target | #### 用法说明 用于实现自定规则的卸载脚本,覆盖被应用的target的默认卸载行为, 例如: ```lua rule("markdown") on_uninstall(function (target) end) ``` ## on\_build\_file * 自定义编译脚本,一次处理一个源文件 #### 函数原型 ::: tip API ```lua on_build_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译文件脚本函数,参数为target、sourcefile和opt | #### 用法说明 ```lua rule("markdown") on_build_file(function (target, sourcefile, opt) print("%%%d: %s", opt.progress, sourcefile) end) ``` 其中第三个参数opt是可选参数,用于获取一些编译过程中的信息状态,例如:opt.progress 为当期的编译进度。 ## on\_buildcmd\_file * 自定义批处理编译脚本,一次处理一个源文件 #### 函数原型 ::: tip API ```lua on_buildcmd_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 批处理编译文件脚本函数,参数为target、batchcmds、sourcefile和opt | #### 用法说明 这是 2.5.2 版本新加的接口,里面的脚本不会直接构建源文件,但是会通过 batchcmds 对象,构造一个批处理命令行任务, xmake 在实际执行构建的时候,一次性执行这些命令。 这对于 `xmake project` 此类工程生成器插件非常有用,因为生成器生成的第三方工程文件并不支持 `on_build_files` 此类内置脚本的执行支持。 但是 `on_buildcmd_files` 构造的最终结果,就是一批原始的 cmd 命令行,可以直接给其他工程文件作为 custom commands 来执行。 另外,相比 `on_build_files`,它也简化对扩展文件的编译实现,更加的可读易配置,对用户也更加友好。 ```lua rule("foo") set_extensions(".xxx") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}) batchcmds:add_depfiles("/xxxxx/dependfile.h", ...) -- batchcmds:add_depvalues(...) -- batchcmds:set_depmtime(os.mtime(...)) -- batchcmds:set_depcache("xxxx.d") end) ``` 除了 `batchcmds:vrunv`,我们还支持一些其他的批处理命令,例如: ```lua batchcmds:show("hello %s", "xmake") batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}, {envs = {LD_LIBRARY_PATH="/xxx"}}) batchcmds:mkdir("/xxx") -- and cp, mv, rm, ln .. batchcmds:compile(sourcefile_cx, objectfile, {configs = {includedirs = sourcefile_dir, languages = (sourcekind == "cxx" and "c++11")}}) batchcmds:link(objectfiles, targetfile, {configs = {linkdirs = ""}}) ``` 同时,我们在里面也简化对依赖执行的配置,下面是一个完整例子: ```lua rule("lex") set_extensions(".l", ".ll") on_buildcmd_file(function (target, batchcmds, sourcefile_lex, opt) -- imports import("lib.detect.find_tool") -- get lex local lex = assert(find_tool("flex") or find_tool("lex"), "lex not found!") -- get c/c++ source file for lex local extension = path.extension(sourcefile_lex) local sourcefile_cx = path.join(target:autogendir(), "rules", "lex_yacc", path.basename(sourcefile_lex) .. (extension == ".ll" and ".cpp" or ".c")) -- add objectfile local objectfile = target:objectfile(sourcefile_cx) table.insert(target:objectfiles(), objectfile) -- add commands batchcmds:show_progress(opt.progress, "${color.build.object}compiling.lex %s", sourcefile_lex) batchcmds:mkdir(path.directory(sourcefile_cx)) batchcmds:vrunv(lex.program, {"-o", sourcefile_cx, sourcefile_lex}) batchcmds:compile(sourcefile_cx, objectfile) -- add deps batchcmds:add_depfiles(sourcefile_lex) local dependfile = target:dependfile(objectfile) batchcmds:set_depmtime(os.mtime(dependfile)) batchcmds:set_depcache(dependfile) end) ``` `add_depfiles` 设置这个目标文件依赖的源文件。`set_depmtime` 设置目标文件的修改时间,如果有任意源文件的修改时间大于它,则认为需要重新生成这个目标文件。这里使用 dependfile 而不是 objectfile 的原因见 [issues 748](https://github.com/xmake-io/xmake/issues/748)。`set_depcache` 设置存储依赖信息的文件。 关于这个的详细说明和背景,见:[issue 1246](https://github.com/xmake-io/xmake/issues/1246) ## on\_build\_files * 自定义编译脚本,一次处理多个源文件 #### 函数原型 ::: tip API ```lua on_build_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译文件脚本函数,参数为target、sourcebatch和opt | #### 用法说明 大部分的自定义构建规则,每次都是处理单独一个文件,输出一个目标文件,例如: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) ``` ## on\_buildcmd\_files * 自定义批处理编译脚本,一次处理多个源文件 #### 函数原型 ::: tip API ```lua on_buildcmd_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 批处理编译文件脚本函数,参数为target、batchcmds、sourcebatch和opt | #### 用法说明 关于这个的详细说明,见:[on\_buildcmd\_file](#on_buildcmd_file) ```lua rule("foo") set_extensions(".xxx") on_buildcmd_files(function (target, batchcmds, sourcebatch, opt) for _, sourcefile in ipairs(sourcebatch.sourcefiles) do batchcmds:vrunv("gcc", {"-o", objectfile, "-c", sourcefile}) end end) ``` ## before\_config * 自定义配置前脚本 #### 函数原型 ::: tip API ```lua before_config(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 配置前脚本函数,参数为target | #### 用法说明 用于实现自定义 target 配置前的执行脚本,例如: ```lua rule("test") before_config(function (target) end) ``` 它会在 on\_config 之前被执行。 ## before\_link * 自定义链接前脚本 #### 函数原型 ::: tip API ```lua before_link(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 链接前脚本函数,参数为target | #### 用法说明 用于实现自定义target链接前的执行脚本,例如: ```lua rule("test") before_link(function (target) end) ``` ## before\_build * 自定义编译前脚本 #### 函数原型 ::: tip API ```lua before_build(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译前脚本函数,参数为target | #### 用法说明 用于实现自定义target构建前的执行脚本,例如: ```lua rule("markdown") before_build(function (target) end) ``` ## before\_clean * 自定义清理前脚本 #### 函数原型 ::: tip API ```lua before_clean(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 清理前脚本函数,参数为target | #### 用法说明 用于实现自定义target清理前的执行脚本,例如: ```lua rule("markdown") before_clean(function (target) end) ``` ## before\_package * 自定义打包前脚本 #### 函数原型 ::: tip API ```lua before_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包前脚本函数,参数为target | #### 用法说明 用于实现自定义target打包前的执行脚本, 例如: ```lua rule("markdown") before_package(function (target) end) ``` ## before\_install * 自定义安装前脚本 #### 函数原型 ::: tip API ```lua before_install(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装前脚本函数,参数为target | #### 用法说明 用于实现自定义target安装前的执行脚本,例如: ```lua rule("markdown") before_install(function (target) end) ``` ## before\_uninstall * 自定义卸载前脚本 #### 函数原型 ::: tip API ```lua before_uninstall(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载前脚本函数,参数为target | #### 用法说明 用于实现自定义target卸载前的执行脚本,例如: ```lua rule("markdown") before_uninstall(function (target) end) ``` ## before\_build\_file * 自定义编译前脚本,一次处理一个源文件 #### 函数原型 ::: tip API ```lua before_build_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译前文件脚本函数,参数为target、sourcefile和opt | #### 用法说明 跟[on\_build\_file](#on_build_file)用法类似,不过这个接口被调用的时机是在编译某个源文件之前, 一般用于对某些源文件进行编译前的预处理。 ## before\_buildcmd\_file * 自定义编译前批处理脚本,一次处理一个源文件 #### 函数原型 ::: tip API ```lua before_buildcmd_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译前批处理文件脚本函数,参数为target、batchcmds、sourcefile和opt | #### 用法说明 跟[on\_buildcmd\_file](#on_buildcmd_file)用法类似,不过这个接口被调用的时机是在编译某个源文件之前, 一般用于对某些源文件进行编译前的预处理。 ## before\_build\_files * 自定义编译前脚本,一次处理多个源文件 #### 函数原型 ::: tip API ```lua before_build_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译前文件脚本函数,参数为target、sourcebatch和opt | #### 用法说明 跟[on\_build\_files](#on_build_files)用法类似,不过这个接口被调用的时机是在编译某些源文件之前, 一般用于对某些源文件进行编译前的预处理。 ## before\_buildcmd\_files * 自定义编译前批处理脚本,一次处理多个源文件 #### 函数原型 ::: tip API ```lua before_buildcmd_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译前批处理文件脚本函数,参数为target、batchcmds、sourcebatch和opt | #### 用法说明 跟[on\_buildcmd\_files](#on_buildcmd_files)用法类似,不过这个接口被调用的时机是在编译某些源文件之前, 一般用于对某些源文件进行编译前的预处理。 ## after\_config * 自定义配置后脚本 #### 函数原型 ::: tip API ```lua after_config(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 配置后脚本函数,参数为target | #### 用法说明 用于实现自定义 target 配置后的执行脚本,例如: ```lua rule("test") after_config(function (target) end) ``` 它会在 on\_config 之后被执行。 ## after\_link * 自定义链接后脚本 #### 函数原型 ::: tip API ```lua after_link(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 链接后脚本函数,参数为target | #### 用法说明 用于实现自定义target链接后的执行脚本,用法跟[before\_link](#before_link)类似。 ## after\_build * 自定义编译后脚本 #### 函数原型 ::: tip API ```lua after_build(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译后脚本函数,参数为target | #### 用法说明 用于实现自定义target构建后的执行脚本,用法跟[before\_build](#before_build)类似。 ## after\_clean * 自定义清理后脚本 #### 函数原型 ::: tip API ```lua after_clean(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 清理后脚本函数,参数为target | #### 用法说明 用于实现自定义target清理后的执行脚本,用法跟[before\_clean](#before_clean)类似。 ## after\_package * 自定义打包后脚本 #### 函数原型 ::: tip API ```lua after_package(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 打包后脚本函数,参数为target | #### 用法说明 用于实现自定义target打包后的执行脚本, 用法跟[before\_package](#before_package)类似。 ## after\_install * 自定义安装后脚本 #### 函数原型 ::: tip API ```lua after_install(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 安装后脚本函数,参数为target | #### 用法说明 用于实现自定义target安装后的执行脚本,用法跟[before\_install](#before_install)类似。 ## after\_uninstall * 自定义卸载后脚本 #### 函数原型 ::: tip API ```lua after_uninstall(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 卸载后脚本函数,参数为target | #### 用法说明 用于实现自定义target卸载后的执行脚本,用法跟[before\_uninstall](#before_uninstall)类似。 ## after\_build\_file * 自定义编译后脚本,一次处理一个源文件 #### 函数原型 ::: tip API ```lua after_build_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译后文件脚本函数,参数为target、sourcefile和opt | #### 用法说明 跟[on\_build\_file](#on_build_file)用法类似,不过这个接口被调用的时机是在编译某个源文件之后, 一般用于对某些编译后对象文件进行后期处理。 ## after\_buildcmd\_file * 自定义编译后批处理脚本,一次处理一个源文件 #### 函数原型 ::: tip API ```lua after_buildcmd_file(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译后批处理文件脚本函数,参数为target、batchcmds、sourcefile和opt | #### 用法说明 跟[on\_buildcmd\_file](#on_buildcmd_file)用法类似,不过这个接口被调用的时机是在编译某个源文件之后, 一般用于对某些编译后对象文件进行后期处理。 ## after\_build\_files * 自定义编译后脚本,一次处理多个源文件 #### 函数原型 ::: tip API ```lua after_build_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译后文件脚本函数,参数为target、sourcebatch和opt | #### 用法说明 跟[on\_build\_files](#on_build_files)用法类似,不过这个接口被调用的时机是在编译某些源文件之后, 一般用于对某些编译后对象文件进行后期处理。 ## after\_buildcmd\_files * 自定义编译后批处理脚本,一次处理多个源文件 #### 函数原型 ::: tip API ```lua after_buildcmd_files(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 编译后批处理文件脚本函数,参数为target、batchcmds、sourcebatch和opt | #### 用法说明 跟[on\_buildcmd\_files](#on_buildcmd_files)用法类似,不过这个接口被调用的时机是在编译某些源文件之后, 一般用于对某些编译后对象文件进行后期处理。 ## rule\_end * 结束定义规则 #### 函数原型 ::: tip API ```lua rule_end() ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | - | 无参数 | #### 用法说明 这个是可选的,如果想要手动结束rule的定义,可以调用它: ```lua rule("test") -- .. rule_end() ``` --- --- url: /zh/guide/project-configuration/custom-rule.md --- # 自定义规则 {#custom-rule} Xmake 不仅原生支持多语言文件的构建,还允许用户通过自定义构建规则实现复杂的未知文件构建。自定义规则可以让你为特定文件类型定义专门的构建逻辑。 ## 基础概念 {#basic-concepts} 自定义构建规则使用 `rule()` 函数定义,通过 `set_extensions()` 将一组文件扩展名关联到规则。一旦这些扩展与规则相关联,对 `add_files()` 的调用将自动使用此自定义规则。 ## 创建简单规则 {#create-simple-rule} ### 基本语法 ```lua rule("rulename") set_extensions(".ext1", ".ext2") on_build_file(function (target, sourcefile, opt) -- 构建逻辑 end) ``` ### 示例:Markdown 转 HTML ```lua -- 定义 markdown 文件的构建规则 rule("markdown") set_extensions(".md", ".markdown") on_build_file(function (target, sourcefile, opt) import("core.project.depend") -- 确保构建目录存在 os.mkdir(target:targetdir()) -- 将 .md 替换为 .html local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- 只在文件改变时重新构建 depend.on_changed(function () -- 调用 pandoc 将 markdown 转换为 html os.vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) end, {files = sourcefile}) end) target("test") set_kind("object") add_rules("markdown") add_files("src/*.md") ``` ## 应用规则到目标 {#apply-rules-to-target} ### 方法一:使用 add\_rules() ```lua target("test") set_kind("binary") add_rules("markdown") -- 应用 markdown 规则 add_files("src/*.md") -- 自动使用 markdown 规则处理 ``` ### 方法二:在 add\_files 中指定 ```lua target("test") set_kind("binary") add_files("src/*.md", {rule = "markdown"}) -- 为特定文件指定规则 ``` ::: tip 注意 通过 `add_files("*.md", {rule = "markdown"})` 方式指定的规则,优先级高于 `add_rules("markdown")` 设置的规则。 ::: ## 规则生命周期 {#rule-lifecycle} 自定义规则支持完整的构建生命周期,可以在不同阶段执行自定义逻辑: ### 主要阶段 * **on\_load**: 规则加载时执行 * **on\_config**: 配置完成后执行 * **before\_build**: 构建前执行 * **on\_build**: 构建时执行(覆盖默认构建行为) * **after\_build**: 构建后执行 * **on\_clean**: 清理时执行 * **on\_package**: 打包时执行 * **on\_install**: 安装时执行 ### 示例:完整生命周期 ```lua rule("custom") set_extensions(".custom") on_load(function (target) -- 规则加载时的配置 target:add("defines", "CUSTOM_RULE") end) before_build(function (target) -- 构建前的准备工作 print("准备构建自定义文件...") end) on_build_file(function (target, sourcefile, opt) -- 处理单个源文件 print("构建文件:", sourcefile) end) after_build(function (target) -- 构建后的清理工作 print("自定义构建完成") end) ``` ## 文件处理方式 {#file-processing-methods} ### 单文件处理 (on\_build\_file) ```lua rule("single") set_extensions(".single") on_build_file(function (target, sourcefile, opt) -- 处理单个文件 local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".out") os.cp(sourcefile, targetfile) end) ``` ### 批量文件处理 (on\_build\_files) ```lua rule("batch") set_extensions(".batch") on_build_files(function (target, sourcebatch, opt) -- 批量处理多个文件 for _, sourcefile in ipairs(sourcebatch.sourcefiles) do print("处理文件:", sourcefile) end end) ``` ## 批处理命令模式 {#batch-command-mode} 使用 `on_buildcmd_file` 和 `on_buildcmd_files` 可以生成批处理命令,而不是直接执行构建: ```lua rule("markdown") set_extensions(".md", ".markdown") on_buildcmd_file(function (target, batchcmds, sourcefile, opt) -- 确保构建目录存在 batchcmds:mkdir(target:targetdir()) -- 生成目标文件路径 local targetfile = path.join(target:targetdir(), path.basename(sourcefile) .. ".html") -- 添加 pandoc 命令 batchcmds:vrunv('pandoc', {"-s", "-f", "markdown", "-t", "html", "-o", targetfile, sourcefile}) -- 添加依赖文件 batchcmds:add_depfiles(sourcefile) end) ``` ## 规则依赖 {#rule-dependencies} ### 添加规则依赖 ```lua rule("foo") add_deps("bar") -- foo 依赖 bar 规则 rule("bar") set_extensions(".bar") on_build_file(function (target, sourcefile, opt) -- bar 规则的构建逻辑 end) ``` ### 控制执行顺序 ```lua rule("foo") add_deps("bar", {order = true}) -- 确保 bar 在 foo 之前执行 on_build_file(function (target, sourcefile, opt) -- foo 规则的构建逻辑 end) ``` ## 常用接口 {#common-interfaces} ### 设置文件扩展名 ```lua rule("myrule") set_extensions(".ext1", ".ext2", ".ext3") ``` ### 添加导入模块 ```lua rule("myrule") add_imports("core.project.depend", "utils.progress") on_build_file(function (target, sourcefile, opt) -- 可以直接使用 depend 和 progress 模块 end) ``` ### 获取构建信息 ```lua rule("myrule") on_build_file(function (target, sourcefile, opt) print("目标名称:", target:name()) print("源文件:", sourcefile) print("构建进度:", opt.progress) print("目标目录:", target:targetdir()) end) ``` ## 实际应用示例 {#practical-examples} ### 示例 1:资源文件处理 ```lua rule("resource") set_extensions(".rc", ".res") on_build_file(function (target, sourcefile, opt) import("core.project.depend") local targetfile = target:objectfile(sourcefile) depend.on_changed(function () os.vrunv("windres", {sourcefile, "-o", targetfile}) end, {files = sourcefile}) end) ``` ### 示例 2:协议缓冲区编译 ```lua rule("protobuf") set_extensions(".proto") on_build_file(function (target, sourcefile, opt) import("core.project.depend") local targetfile = path.join(target:autogendir(), path.basename(sourcefile) .. ".pb.cc") depend.on_changed(function () os.vrunv("protoc", {"--cpp_out=" .. target:autogendir(), sourcefile}) end, {files = sourcefile}) -- 将生成的文件添加到目标 target:add("files", targetfile) end) ``` ## 最佳实践 {#best-practices} 1. **使用依赖检查**: 通过 `depend.on_changed()` 避免不必要的重新构建 2. **错误处理**: 在规则中添加适当的错误处理逻辑 3. **进度显示**: 使用 `opt.progress` 显示构建进度 4. **模块化**: 将复杂规则拆分为多个简单规则 5. **文档化**: 为自定义规则添加清晰的注释和文档 ## 更多信息 {#more-information} * 完整的 API 文档:[自定义规则 API](/zh/api/description/custom-rule) * 内置规则参考:[内置规则](/zh/api/description/builtin-rules) * 规则示例:[规则示例](/zh/examples/cpp/protobuf) --- --- url: /zh/guide/project-configuration/syntax-description.md --- # 语法描述 {#syntax-description} Xmake 的工程描述文件xmake.lua虽然基于lua语法,但是为了使得更加方便简洁得编写项目构建逻辑,xmake对其进行了一层封装,使得编写xmake.lua不会像写makefile那样繁琐 基本上写个简单的工程构建描述,只需三行就能搞定,例如: ```lua target("test") set_kind("binary") add_files("src/*.c") ``` ## 配置分离 xmake.lua采用二八原则实现了描述域、脚本域两层分离式配置。 什么是二八原则呢,简单来说,大部分项目的配置,80%的情况下,都是些基础的常规配置,比如:`add_cxflags`, `add_links`等, 只有剩下不到20%的地方才需要额外做些复杂来满足一些特殊的配置需求。 而这剩余的20%的配置通常比较复杂,如果直接充斥在整个xmake.lua里面,会把整个项目的配置整个很混乱,非常不可读。 因此,xmake通过描述域、脚本域两种不同的配置方式,来隔离80%的简单配置以及20%的复杂配置,使得整个xmake.lua看起来非常的清晰直观,可读性和可维护性都达到最佳。 ### 描述域 {#description-scope} 对于刚入门的新手用户,或者仅仅是维护一些简单的小项目,通过完全在描述配置就已经完全满足需求了,那什么是描述域呢?它长这样: ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") add_syslinks("pthread") ``` 一眼望去,其实就是个 `set_xxx`/`add_xxx`的配置集,对于新手,完全可以不把它当做lua脚本,仅仅作为普通的,但有一些基础规则的配置文件就行了。 如果因为,看着有括号,还是像脚本语言的函数调用,那我们也可以这么写(是否带括号看个人喜好): ```lua target "test"     set_kind "binary"     add_files "src/*.c"     add_defines "DEBUG"     add_syslinks "pthread" ``` 这是不是看着更像配置文件了?其实描述域就是配置文件,类似像json等key/values的配置而已,所以即使完全不会lua的新手,也是能很快上手的。 而且,对于通常的项目,仅通过`set_xxx/add_xxx`去配置各种项目设置,已经完全满足需求了。 这也就是开头说的:80%的情况下,可以用最简单的配置规则去简化项目的配置,提高可读性和可维护性,这样对用户和开发者都会非常的友好,也更加直观。 如果我们要针对不同平台,架构做一些条件判断怎么办?没关系,描述域除了基础配置,也是支持条件判断,以及for循环的: ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") if is_plat("linux", "macosx") then add_links("pthread", "m", "dl") end ``` ```lua target("test") set_kind("binary") add_files("src/*.c") add_defines("DEBUG") for _, name in ipairs({"pthread", "m", "dl"}) do add_links(name) end ``` 这是不是看着有点像lua了?虽说,平常可以把它当做普通配置问题,但是xmake毕竟基于lua,所以描述域还是支持lua的基础语言特性的。 ::: tip 注意 不过需要注意的是,描述域虽然支持lua的脚本语法,但在描述域尽量不要写太复杂的lua脚本,比如一些耗时的函数调用和for循环 ::: 并且在描述域,主要目的是为了设置配置项,因此xmake并没有完全开放所有的模块接口,很多接口在描述域是被禁止调用的, 即使开放出来的一些可调用接口,也是完全只读的,不耗时的安全接口,比如:`os.getenv()`等读取一些常规的系统信息,用于配置逻辑的控制。 ::: tip 注意 另外需要注意一点,xmake.lua是会被多次解析的,用于在不同阶段解析不同的配置域:比如:`option()`, `target()`等域。 ::: 因此,不要想着在xmake.lua的描述域,写复杂的lua脚本,也不要在描述域调用print去显示信息,因为会被执行多遍,记住:会被执行多遍!!! ### 脚本域 {#script-scope} 限制描述域写复杂的lua,各种lua模块和接口都用不了?怎么办?这个时候就是脚本域出场的时候了。 如果用户已经完全熟悉了xmake的描述域配置,并且感觉有些满足不了项目上的一些特殊配置维护了,那么我们可以在脚本域做更加复杂的配置逻辑: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load(function (target) if is_plat("linux", "macosx") then target:add("links", "pthread", "m", "dl") end end) after_build(function (target) import("core.project.config") local targetfile = target:targetfile() os.cp(targetfile, path.join(config.buildir(), path.filename(targetfile))) print("build %s", targetfile) end) ``` 只要是类似:`on_xxx`, `after_xxx`, `before_xxx`等字样的function body内部的脚本,都属于脚本域。 在脚本域中,用户可以干任何事,xmake提供了import接口可以导入xmake内置的各种lua模块,也可以导入用户提供的lua脚本。 我们可以在脚本域实现你想实现的任意功能,甚至写个独立项目出来都是可以的。 对于一些脚本片段,不是很臃肿的话,像上面这么内置写写就足够了,如果需要实现更加复杂的脚本,不想充斥在一个xmake.lua里面,可以把脚本分离到独立的lua文件中去维护。 例如: ```lua target("test") set_kind("binary") add_files("src/*.c") on_load("modules.test.load") on_install("modules.test.install") ``` 我们可以把自定义的脚本放置到xmake.lua对应目录下,`modules/test/load.lua`和`modules/test/install.lua`中独立维护。 这些独立的lua脚本里面,我们还可以通过[import](/zh/api/scripts/builtin-modules/import)导入各种内置模块和自定义模块进来使用,就跟平常写lua, java没啥区别。 而对于脚本的域的不同阶段,`on_load`主要用于target加载时候,做一些动态化的配置,这里不像描述域,只会执行一遍哦!!! 其他阶段,还有很多,比如:`on/after/before`\_`build/install/package/run`等,具体看下后面的target api手册部分吧,这里就不细说了。 ## 配置类型 {#configuration-type} 在描述域配置中,分配置域和配置项,配置域里面可以通过`set_xxx`/`add_xxx`的接口,配置各种配置项。 ```lua target("test1") set_kind("binary") add_files("src/*.c") target("test2") set_kind("binary") add_files("src/*.c") ``` 像上述配置中,target就属于配置域,它下面的所有`set_xx`/`add_xxx`接口配置都属于配置项,对这个target局部生效。 我们可以把它理解成局部作用域,类似c里面的block块: ``` target("test1") { set_kind("binary") add_files("src/*.c") } target("test2") { set_kind("binary") add_files("src/*.c") } ``` 不过,为了简化写法,xmake约定每个新定义的target域开始,上一个配置域就自动结束了,当然,如果这样用户觉得有困扰,也可以手动配置离开域: ```lua target("test1") set_kind("binary") add_files("src/*.c") target_end() target("test2") set_kind("binary") add_files("src/*.c") target_end() ``` ### 配置域 目前提供的配置域有:`target()`, `option()`, `task()`, `package()` 每个域的详细说明,见:[API手册](/zh/api/description/specification) ### 配置项 只要是带有`set_xxx`和`add_xxx`字样的配置,都属于配置项,一个配置域里面可以设置多个配置项。 关于配置项的规范说明,见:[接口规范](/zh/api/description/specification) ## 作用域 {#scope} Xmake 的描述语法是按作用域划分的,主要分为: * 外部作用域 * 内部作用域 * 接口作用域 那哪些属于外部,哪些又属于内部呢,看看下面的注释,就知道个大概了: ```lua -- 外部作用域 target("test") -- 外部作用域 set_kind("binary") add_files("src/*.c") on_run(function () -- 内部作用域 end) after_package(function () -- 内部作用域 end) -- 外部作用域 task("hello") -- 外部作用域 on_run(function () -- 内部作用域 end) ``` 简单的说,就是在自定义脚本`function () end`之内的都属于内部作用域,也就是脚本作用域,其他地方都是都属于于外部作用域。。 ### 外部作用域 对于大部分工程来说,并不需要很复杂的工程描述,也不需要自定义脚本支持,只需要简单的 `set_xxx` 或者 `add_xxx` 就能满足需求了 那么根据二八定律,80%的情况下,我们只需要这么写: ```lua target("test") set_kind("static") add_files("src/test/*.c") target("demo") add_deps("test") set_kind("binary") add_links("test") add_files("src/demo/*.c") ``` 不需要复杂的api调用,也不需要各种繁琐的变量定义,以及 if 判断 和 for 循环,要的就是简洁可读,一眼看过去,就算不懂lua语法也没关系 就当做简单的描述语法,看上去有点像函数调用而已,会点编程的基本一看就知道怎么配置。 为了做到简洁、安全,在这个作用域内,很多lua 内置api是不开放出来的,尤其是跟写文件、修改操作环境相关的,仅仅提供一些基本的只读接口,和逻辑操作 目前外部作用域开放的lua内置api有: * table * string * pairs * ipairs * print * os 当然虽然内置lua api提供不多,但xmake还提供了很多扩展api,像描述api就不多说,详细可参考:[API手册](/zh/api/scripts/builtin-modules/import) 还有些辅助api,例如: dirs:扫描获取当前指定路径中的所有目录 files:扫描获取当前指定路径中的所有文件 format: 格式化字符串,string.format的简写版本 还有变量定义、逻辑操作也是可以使用的,毕竟是基于lua的,该有的基础语法,还是要有的,我们可以通过if来切换编译文件: ```lua target("test") set_kind("static") if is_plat("iphoneos") then add_files("src/test/ios/*.c") else add_files("src/test/*.c") end ``` 需要注意的是,变量定义分全局变量和局部变量,局部变量只对当前xmake.lua有效,不影响子xmake.lua ```lua -- 局部变量,只对当前xmake.lua有效 local var1 = 0 -- 全局变量,影响所有之后 includes() 包含的子 xmake.lua var2 = 1 includes("src") ``` ### 内部作用域 也称插件、脚本作用域,提供更加复杂、灵活的脚本支持,一般用于编写一些自定义脚本、插件开发、自定义task任务、自定义模块等等 一般通过`function () end`包含,并且被传入到`on_xxx`, `before_xxx`和`after_xxx`接口内的,都属于自作用域。 例如: ```lua -- 自定义脚本 target("hello") after_build(function () -- 内部作用域 end) -- 自定义任务、插件 task("hello") on_run(function () -- 内部作用域 end) ``` 在此作用域中,不仅可以使用大部分lua的api,还可以使用很多xmake提供的扩展模块,所有扩展模块,通过 import 来导入。 具体可参考:[import 模块导入文档](/zh/api/scripts/builtin-modules/import). 这里我们给个简单的例子,在编译完成后,对ios目标程序进行ldid签名: ```lua target("iosdemo") set_kind("binary") add_files("*.m") after_build(function (target) -- 执行签名,如果失败,自动中断,给出高亮错误信息 os.run("ldid -S$(projectdir)/entitlements.plist %s", target:targetfile()) end) ``` 需要注意的是,在内部作用域中,所有的调用都是启用异常捕获机制的,如果运行出错,会自动中断xmake,并给出错误提示信息 因此,脚本写起来,不需要繁琐的`if retval then`判断,脚本逻辑更加一目了然 ### 接口作用域 在外部作用域中的所有描述api设置,本身也是有作用域之分的,在不同地方调用,影响范围也不相同,例如: ```lua -- 全局根作用域,影响所有target,包括includes() 中的子工程target设置 add_defines("DEBUG") -- 定义或者进入demo目标作用域(支持多次进入来追加设置) target("demo") set_kind("shared") add_files("src/*.c") -- 当前target作用域,仅仅影响当前target add_defines("DEBUG2") -- 选项设置,仅支持局部设置,不受全局api设置所影响 option("test") -- 当前选项的局部作用域 set_default(false) -- 其他target设置,-DDEBUG 也会被设置上 target("demo2") set_kind("binary") add_files("src/*.c") -- 重新进入demo目标作用域 target("demo") -- 追加宏定义,只对当前demo目标有效 add_defines("DEBUG3") ``` 通常情况下,进入另一个target/option域设置,会自动离开上个target/option域,但是有时候为了比较一些作用域污染情况,我们可以显示离开某个域,例如: ```lua option("test") set_default(false) option_end() target("demo") set_kind("binary") add_files("src/*.c") target_end() ``` 调用`option_end()`, `target_end()`即可显式的离开当前target/option域设置。 ### 作用域缩进 xmake.lua里面缩进,只是个编写规范,用于更加清楚的区分,当前的设置 是针对 那个作用域的,虽然就算不缩进,也一样ok,但是可读性上 并不是很好。。 例如: ```lua target("xxxx") set_kind("binary") add_files("*.c") ``` 和 ```lua target("xxxx") set_kind("binary") add_files("*.c") ``` 上述两种方式,效果上都是一样的,但是理解上,第一种更加直观,一看就知道`add_files`仅仅只是针对 target 设置的,并不是全局设置 因此,适当的进行缩进,有助于更好的维护xmake.lua 最后附上,tbox的[xmake.lua](https://github.com/tboox/tbox/blob/master/src/tbox/xmake.lua)描述,仅供参考。。 ### 代码格式化 由于默认的描述域配置语法的缩进并不符合 lua 格式规范,因此 lua language server 是不支持对它进行格式化处理的。 如果想要让 IDE,编辑器更好的对配置进行格式化缩进支持,我么可以通过 `do end` 的写法来处理: ```lua target("bar") do set_kind("binary") add_files("src/*.cpp") end target("foo") do set_kind("binary") add_files("src/*.cpp") end ``` 这样,Lua LSP 就能把它作为标准的 lua 代码进行正确的格式化,是否需要这么做,看用户自己的需求。 如果没有代码自动格式化的使用习惯,那就不需要这么做。 ## 语法简化 xmake.lua的配置域语法,非常灵活,可以在相关域做各种复杂灵活的配置,但是对于许多精简的小块配置,这个时候就稍显冗余了: ```lua option("test1") set_default(true) set_showmenu(true) set_description("test1 option") option("test2") set_default(true) set_showmeu(true) option("test3") set_default("hello") ``` xmake 2.2.6以上版本,对于上面的这些小块option域设置,我们可以简化下成单行描述: ```lua option("test1", {default = true, showmenu = true, description = "test1 option"}) option("test2", {default = true, showmenu = true}) option("test3", {default = "hello"}) ``` 除了option域,对于其他域也是支持这种简化写法的,例如: ```lua target("demo") set_kind("binary") add_files("src/*.c") ``` 简化为: ```lua target("demo", {kind = "binary", files = "src/*.c"}) ``` 或者 ```lua target("demo", { kind = "binary", files = "src/*.c" }) ``` 当然,如果配置需求比较复杂的,还是原有的多行设置方式更加方便,这个就看自己的需求来评估到底使用哪种方式了。 ## 可选的域配置语法 我们默认约定的域配置语法,尽管非常简洁,但是对自动格式化缩进和IDE不是很友好。 ```lua target("foo") set_kind("binary") add_files("src/*.cpp") target_end() ``` 另外,它不能自动结束当前 target 作用域,用户需要显式调用 `target_end()`。 虽然,上面我们提到,可以使用 `do end` 模式来解决自动缩进问题,但是需要 `target_end()` 的问题还是存在。 ```lua target("bar") do set_kind("binary") add_files("src/*.cpp") end target_end() ``` 在 2.7.3 版本中,我们提供了一种更好的可选域配置语法,来解决自动缩进,target 域隔离问题,例如: ```lua add_defines("ROOT") target("foo", function () set_kind("binary") add_files("src/*.cpp") add_defines("FOO") end) target("bar", function () set_kind("binary") add_files("src/*.cpp") add_defines("BAR") end) ``` foo 和 bar 两个域是完全隔离的,我们即使在它们中间配置其他设置,也不会影响它们,另外,它还对 LSP 非常友好。 --- --- url: /zh/api/description/helper-interfaces.md --- # 辅助接口 另外,此接口在 2.2.5 之后的版本,提供了一些内置的辅助函数,可以直接使用 includes 导入,具体有哪些内置函数可以看下:[Helper functions](https://github.com/xmake-io/xmake/tree/master/xmake/includes) 我们可以使用这些接口,检测links, c/c++ type, includes 和 编译器特性,并且写入宏定义到config.h 其中,我们提供了两类接口,`check_xxx` 和 `configvar_check_xxx`,带有 `configvar_` 前缀的接口会在检测通过后,写入 `add_configfiles` 指定的 config.h.in 模板文件。 而 `check_xxx` 仅仅只是定义相关 macros 参与编译,但不会持久化到 `config.h.in` 中去。 相关 issues 见: * [#342](https://github.com/xmake-io/xmake/issues/342) * [#1715](https://github.com/xmake-io/xmake/issues/1715) :::tip 注意 注:如果是 2.8.5 以上版本,就不需要通过 `includes("check_links.lua")` 分别引入这些接口了,而是使用更加方便的 ::: ```lua includes("@builtin/check") ``` 一次性引入所有检测接口,当然我们也可以按需引入单个脚本: ```lua includes("@builtin/check/check_links.lua") ``` 而原有的引入路径,没有区分是否为用户路径,不方便管理维护,且容易被用户配置干扰,后面会逐步废弃。 ## 检测 links 我们可以通过尝试链接来检测指定的 links 是否通过。 ```lua includes("check_links.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_links("HAS_PTHREAD", {"pthread", "m", "dl"}) ``` ```c [config.h.in] ${define HAS_PTHREAD} ``` ```c [config.h] define HAS_PTHREAD 1 /* #undef HAS_PTHREAD */ ``` ## 检测 c/c++ 类型 我们也能够检测 c/c++ 类型是否存在。 `configvar_check_ctypes` 用于检测 c 代码类型,`configvar_check_cxxtypes` 用于检测 c++ 代码类型。 ```lua includes("check_ctypes.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_ctypes("HAS_WCHAR", "wchar_t") configvar_check_ctypes("HAS_WCHAR_AND_FLOAT", {"wchar_t", "float"}) ``` ```c [config.h.in] ${define HAS_WCHAR} ${define HAS_WCHAR_AND_FLOAT} ``` ```c [config.h] /* #undef HAS_WCHAR */ /* #undef HAS_WCHAR_AND_FLOAT */ ``` ## 检测 c/c++ 函数 `configvar_check_cfuncs` 用于检测 c 代码函数,`configvar_check_cxxfuncs` 用于检测 c++ 代码函数。 ```lua includes("check_cfuncs.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_cfuncs("HAS_SETJMP", "setjmp", {includes = {"signal.h", "setjmp.h"}}) ``` ```c [config.h.in] ${define HAS_SETJMP} ``` ```c [config.h] define HAS_SETJMP 1 /* #undef HAS_SETJMP */ ``` ## 检测 c/c++ 头文件 `configvar_check_cincludes` 用于检测 c 代码头文件,`configvar_check_cxxincludes` 用于检测 c++ 代码头文件。 ```lua includes("check_cincludes.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_cincludes("HAS_STRING_H", "string.h") configvar_check_cincludes("HAS_STRING_AND_STDIO_H", {"string.h", "stdio.h"}) ``` ```c [config.h.in] ${define HAS_STRING_H} ${define HAS_STRING_AND_STDIO_H} ``` ```c [config.h] /* #undef HAS_STRING_H */ define HAS_STRING_AND_STDIO_H 1 ``` ## 检测 c/c++ 代码片段 `configvar_check_csnippets` 用于检测 c 代码片段,`configvar_check_cxxsnippets` 用于检测 c++ 代码片段。 ```lua includes("check_csnippets.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_csnippets("HAS_STATIC_ASSERT", "_Static_assert(1, \"\");") ``` ```c [config.h.in] ${define HAS_STATIC_ASSERT} ``` ```c [config.h] define HAS_STATIC_ASSERT 1 ``` v2.5.7 之后对 check\_csnippets 做了改进,新增 `tryrun` 和 `output` 参数去尝试运行和捕获输出。 ```lua includes("check_csnippets.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") check_csnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0 : -1;", {tryrun = true}) check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) configvar_check_csnippets("HAS_LONG_8", "return (sizeof(long) == 8)? 0 : -1;", {tryrun = true}) configvar_check_csnippets("PTR_SIZE", 'printf("%d", sizeof(void*)); return 0;', {output = true, number = true}) ``` 如果启用捕获输出,`config.h.in` 的 `${define PTR_SIZE}` 会自动生成 `#define PTR_SIZE 4`。 其中,`number = true` 设置,可以强制作为 number 而不是字符串值,否则默认会定义为 `#define PTR_SIZE "4"` ## 检测编译器特性 ```lua includes("check_features.lua") target("test") set_kind("binary") add_files("*.c") add_configfiles("config.h.in") configvar_check_features("HAS_CONSTEXPR", "cxx_constexpr") configvar_check_features("HAS_CONSEXPR_AND_STATIC_ASSERT", {"cxx_constexpr", "c_static_assert"}, {languages = "c++11"}) ``` ```c [config.h.in] ${define HAS_CONSTEXPR} ${define HAS_CONSEXPR_AND_STATIC_ASSERT} ``` ```c [config.h] /* #undef HAS_CONSTEXPR */ define HAS_CONSEXPR_AND_STATIC_ASSERT 1 ``` 所有 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 | v2.5.9 新增 c++17 特性检测: | 特性名 | | ------------------------------------ | | cxx\_aggregate\_bases | | cxx\_aligned\_new | | cxx\_capture\_star\_this | | cxx\_constexpr | | cxx\_deduction\_guides | | cxx\_enumerator\_attributes | | cxx\_fold\_expressions | | cxx\_guaranteed\_copy\_elision | | cxx\_hex\_float | | cxx\_if\_constexpr | | cxx\_inheriting\_constructors | | cxx\_inline\_variables | | cxx\_namespace\_attributes | | cxx\_noexcept\_function\_type | | cxx\_nontype\_template\_args | | cxx\_nontype\_template\_parameter\_auto | | cxx\_range\_based\_for | | cxx\_static\_assert | | cxx\_structured\_bindings | | cxx\_template\_template\_args | | cxx\_variadic\_using | v2.5.9 新增 c++20 特性检测: | 特性名 | | ------------------------------------ | | cxx\_aggregate\_paren\_init | | cxx\_char8\_t | | cxx\_concepts | | cxx\_conditional\_explicit | | cxx\_consteval | | cxx\_constexpr | | cxx\_constexpr\_dynamic\_alloc | | cxx\_constexpr\_in\_decltype | | cxx\_constinit | | cxx\_deduction\_guides | | cxx\_designated\_initializers | | cxx\_generic\_lambdas | | cxx\_impl\_coroutine | | cxx\_impl\_destroying\_delete | | cxx\_impl\_three\_way\_comparison | | cxx\_init\_captures | | cxx\_modules | | cxx\_nontype\_template\_args | | cxx\_using\_enum | 2.5.8 之后,新增对 cstd 和 c++ std 版本支持,相关 issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) ```lua configvar_check_features("HAS_CXX_STD_98", "cxx_std_98") configvar_check_features("HAS_CXX_STD_11", "cxx_std_11", {languages = "c++11"}) configvar_check_features("HAS_CXX_STD_14", "cxx_std_14", {languages = "c++14"}) configvar_check_features("HAS_CXX_STD_17", "cxx_std_17", {languages = "c++17"}) configvar_check_features("HAS_CXX_STD_20", "cxx_std_20", {languages = "c++20"}) configvar_check_features("HAS_C_STD_89", "c_std_89") configvar_check_features("HAS_C_STD_99", "c_std_99") configvar_check_features("HAS_C_STD_11", "c_std_11", {languages = "c11"}) configvar_check_features("HAS_C_STD_17", "c_std_17", {languages = "c17"}) ``` ## 检测内置宏定义 编译器存在一些内置的宏定义,比如:`__GNUC__` 等,我们可以通过 `check_macros` 和 `configvar_check_macros` 辅助脚本来检测它们是否存在。 相关 issues: [#1715](https://github.com/xmake-io/xmake/issues/1715) ```lua -- 检测宏是否定义 configvar_check_macros("HAS_GCC", "__GNUC__") -- 检测宏没有被定义 configvar_check_macros("NO_GCC", "__GNUC__", {defined = false}) -- 检测宏条件 configvar_check_macros("HAS_CXX20", "__cplusplus >= 202002L", {languages = "c++20"}) ``` ## 检测类型大小 在先前的版本中,我们可以通过 `check_csnippets` 和 `output = true` 的方式,来实现类型检测。 ```lua check_csnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` 但是这种方式,是通过尝试运行测试代码,然后获取运行输出结果,提取类型大小信息。 这对于交叉编译,就不适用了。 在 2.8.5 版本中,我们新增了 `check_sizeof` 辅助接口,可以通过直接解析测试程序的二进制文件,提取类型大小信息。 由于不需要运行测试,这种方式不仅可以支持交叉编译,而且对检测效率也有极大的提升,使用也更加的简单。 ```lua includes("@builtin/check") target("test") set_kind("static") add_files("*.cpp") check_sizeof("LONG_SIZE", "long") check_sizeof("STRING_SIZE", "std::string", {includes = "string"}) ``` ```sh $ xmake f -c checking for LONG_SIZE ... 8 checking for STRING_SIZE ... 24 ``` 另外,我也可以通过 `target:check_sizeof` 在脚本域进行检测。 ## 检测大小端 在 2.8.9 版本之后,我们新增了 `check_bigendian` 接口,来判断当前编译目标是否为大端模式。 ```lua includes("@builtin/check") target("test") set_kind("static") add_files("*.cpp") check_bigendian("IS_BIG_ENDIAN") ``` 如果检测通过,当前是大端模式,那么会定义 `IS_BIG_ENDIAN=1`。 --- --- url: /zh/guide/basic-commands/run-targets.md --- # 运行目标 {#run-targets} Xmake 提供了内置的 `xmake run` 命令,可以快速的运行被构建的目标程序,而无需手动找到对应的可执行文件来运行。 这会提供许多的便利,并且它还会自动绑定运行需要的环境,确保资源、动态库等都能正常被自动加载。 ## 命令格式 ```sh $ xmake run [options] [target] [runargs] ``` :::tip 注意 前面的 options 是 `xmake run` 的参数选项,而后面的 `runargs` 是特定于 target 目标的可执行参数,可以原样传递进 target 目标程序内部。 ::: ## 运行一个程序 通常我们只需要执行 `xmake run` 即可运行所有的可执行程序。 ```sh $ xmake run hello world! ``` ## 运行特定的目标程序 如果要运行指定的目标程序,可以执行: ```sh $ xmake run foo ``` ## 运行所有程序 如果 target 被配置为 `default = false`,那么默认是不会运行它的。 ```lua target("test") set_default(false) add_files("src/*.c") ``` 如果想要运行所有目标, 包括这些 `default = false` 的目标程序,那么可以传递 `-a/--all` 参数。 ```sh $ xmake run -a ``` ## 传递运行参数 我们还可以传递运行参数给内部的目标程序。 ```sh $ xmake run foo --arg1=xxx --arg2=yyy ``` :::tip 注意 这个时候,我们不能省略目标名称,必须指定需要运行的目标名,否则会导致参数歧义。 ::: 我们也可以通过 target 的 [set\_runargs](/zh/api/description/project-target#set-runargs) 配置接口,来指定传入的运行参数,而不需要每次命令行指定它。 ## 设置运行的工作目录 默认情况下,`xmake run` 的工作目录是在可执行文件所在的目录,这有助于它能够方便的找到一些资源和动态库,也能做一些路径隔离,避免一些程序生成文件到当前工程根目录下。 如果我们想修改工作目录,我们可以通过 `-w workdir` 参数来指定。 ```sh $ xmake run -w /tmp foo ``` 我们将 foo 程序的运行目录改成了 /tmp/。 另外,我们也可以通过 target 的 [set\_rundir](/zh/api/description/project-target#set-rundir) 配置接口,来指定传入的运行参数,而不需要每次命令行指定它。 ## 调试程序 我们也可以传递 `-d` 参数,让 `xmake run` 在运行程序的同时,加载当前系统环境可用的调试器,例如:gdb/lldb。 但前提是,当前程序必须是调试模式编译的,否则会因为缺少必要的符号信息,看不到调用栈、行号等信息,不便于调试。 ```sh $ xmake f -m debug $ xmake ``` ```sh $ xmake run -d hello ``` Xmake 将会使用调试器去加载程序运行,目前支持:lldb, gdb, windbg, vsjitdebugger, ollydbg 等各种调试器。 ```sh [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]$ ``` 另外,我们也能够切换使用指定的调试器: ```sh $ xmake f --debugger=gdb $ xmake run -d hello ``` --- --- url: /zh/guide/extras/remote-compilation.md --- # 远程编译 {#remote-compilation} 2.6.5 版本提供了远程编译支持,我们可以通过它在远程服务器上编译代码、远程运行和调试。 服务器可以部署在 Linux/MacOS/Windows 上,实现跨平台编译。例如:在 Linux 上编译运行 Windows 程序,在 Windows 上编译运行 macOS/Linux 程序。 相比 ssh 远程登录编译,它更加稳定,使用更加流畅,不会因为网络不稳定导致 ssh 终端输入卡顿,也可以实现本地快速编辑代码文件。 甚至我们可以在 VS/Sublime/VSCode/IDEA 等编辑器和 IDE 中无缝实现远程编译,而不需要依赖 IDE 本身对远程编译的支持。 ## 开启服务 {#start-service} ```sh $ xmake service : listening 0.0.0.0:9091 .. ``` 我们也可以开启服务的同时,回显详细日志信息。 ```sh $ xmake service -vD : listening 0.0.0.0:9091 .. ``` ## 以 Daemon 模式开启服务 ```sh $ xmake service --start $ xmake service --restart $ xmake service --stop ``` ## 配置服务端 {#configure-server} 我们首先运行 `xmake service` 命令,它会自动生成一个默认的 `server.conf` 配置文件,存储到 `~/.xmake/service/server.conf`。 ::: tip 注意 2.6.5 版本,配置地址在 `~/.xmake/service.conf`,后续版本做了大量改进,分离了配置文件,如果用的是 2.6.6 以上版本,请使用新的配置文件。 ::: 然后,我们编辑它,修改服务器的监听端口(可选)。 ```sh $ cat ~/.xmake/service/server.conf { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f" } } ``` ## 配置客户端 {#configure-client} 客户端配置文件在 `~/.xmake/service/client.conf`,我们可以在里面配置客户端需要连接的服务器地址。 ::: tip 注意 2.6.5 版本,配置地址在 `~/.xmake/service.conf`,后续版本做了大量改进,分离了配置文件,如果用的是 2.6.6 以上版本,请使用新的配置文件。 ::: ```sh $ cat ~/.xmake/service/client.conf { remote_build = { connect = "127.0.0.1:9691", token = "e438d816c95958667747c318f1532c0f" } } ``` ## 用户认证和授权 {#user-authorization} ::: tip 注意 2.6.6 以上版本才支持用户认证,2.6.5 版本只能匿名连接。 ::: 在实际连接之前,我们简单介绍下 xmake 提供的服务目前提供的几种认证机制。 1. Token 认证 2. 密码认证 3. 可信主机验证 ### Token 认证 这也是我们默认推荐的方式,更加安全,配置和连接也更加方便,每次连接也不用输入密码。 我们在执行 `xmake service` 命令时,默认就会生成一个服务端和客户端的配置文件,并且自动生成一个默认的 token,因此本地直连是不需要任何配置的。 #### 服务端认证配置 服务端可以配置多个 token 用于对不同用户主机进行授权连接,当然也可以共用一个 token。 ```sh $ cat ~/.xmake/service/server.conf { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f" } } ``` #### 客户端认证配置 客户端只需要添加服务器上的 token 到对应的客户端配置中即可。 ```sh $ cat ~/.xmake/service/client.conf { remote_build = { connect = "127.0.0.1:9691", token = "e438d816c95958667747c318f1532c0f" } } ``` #### 手动生成新 token 我们也可以执行下面的命令,手动生成一个新的 token,自己添加到服务器配置中。 ```sh $ xmake service --gen-token New token a7b9fc2d3bfca1472aabc38bb5f5d612 is generated! ``` ### 密码认证 我们也提供密码认证的授权模式。相比 token 认证,它需要用户每次连接时输入密码,验证通过后才能连接上。 #### 服务端认证配置 密码认证不需要手动配置 token,只需要执行下面的命令添加用户即可,添加过程中会提示用户输入密码。 ```sh $ xmake service --add-user=ruki Please input user ruki password: 123456 Add user ruki ok! ``` 然后,xmake 就会通过用户名和密码生成一个新的 token,添加到服务器配置的 token 列表中去。 ```sh $ cat ~/.xmake/service/server.conf { known_hosts = { }, logfile = "/Users/ruki/.xmake/service/server/logs.txt", remote_build = { listen = "0.0.0.0:9691", workdir = "/Users/ruki/.xmake/service/server/remote_build" }, tokens = { "e438d816c95958667747c318f1532c0f", "7889e25402413e93fd37395a636bf942" } } ``` 当然,我们也可以删除指定的用户和密码。 ```sh $xmake service --rm-user=ruki Please input user ruki password: 123456 Remove user ruki ok! ``` #### 客户端认证配置 对于客户端,我们不再需要设置服务器的 token,只需要在连接配置中追加需要连接的用户名即可开启密码认证,格式:`user@address:port` ```sh $ cat ~/.xmake/service/client.conf { remote_build = { connect = "root@127.0.0.1:9691" } } ``` ::: tip 注意 如果去掉用户名,也没配置 token,那就是匿名模式。如果服务器也没配置 token,就是完全禁用认证,直接连接。 ::: ### 可信主机验证 另外,为了进一步提高安全性,我们还提供了服务端可信主机验证。如果在服务器配置的 known\_hosts 列表中,配置了可以连接的客户端主机 IP 地址, 那么只有这些主机可以成功连接上这台服务器,其他主机对它的连接都会被提示为不可信而拒绝连接,即使 token 和密码认证都没问题也不行。 ```sh $ cat ~/.xmake/service/server.conf { logfile = "/Users/ruki/.xmake/service/logs.txt", server = { tokens = { "4b928c7563a0cba10ff4c3f5ca0c8e24" }, known_hosts = { "127.0.0.1", "xx.xx.xx.xx"} } } ``` ## 连接远程的服务器 接下来,我们只需要进入需要远程编译的工程根目录,执行 `xmake service --connect` 命令,进行连接。 如果是 token 认证模式,那么不需要的额外的密码输入,直接连接。 ```sh $ xmake create test $ cd test $ xmake service --connect : connect 192.168.56.110:9091 .. : connected! : sync files in 192.168.56.110:9091 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` 如果是密码认证,那么会提示用户输入密码,才能继续连接。 ```sh $ xmake service --connect Please input user root password: 000000 : connect 127.0.0.1:9691 .. : connected! : sync files in 127.0.0.1:9691 .. Scanning files .. Comparing 3 files .. [+]: xmake.lua [+]: .gitignore [+]: src/main.cpp 3 files has been changed! Archiving files .. Uploading files with 1591 bytes .. : sync files ok! ``` 如果密码不对,就会提示错误。 ```sh $ xmake service --connect Please input user root password: 123 : connect 127.0.0.1:9691 .. : connect 127.0.0.1:9691 failed, user and password are incorrect! ``` ## 远程构建工程 连接成功后,我们就可以像正常本地编译一样,进行远程编译。 ```sh $ xmake : run xmake in 192.168.56.110:9091 .. checking for platform ... macosx checking for architecture ... x86_64 checking for Xcode directory ... /Applications/Xcode.app checking for Codesign Identity of Xcode ... Apple Development: waruqi@gmail.com (T3NA4MRVPU) checking for SDK version of Xcode for macosx (x86_64) ... 11.3 checking for Minimal target version of Xcode for macosx (x86_64) ... 11.4 [ 25%]: cache compiling.release src/main.cpp [ 50%]: linking.release test [100%]: build ok! : run command ok! ``` ## 远程运行目标程序 我们也可以像本地运行调试那样,远程运行调试编译的目标程序。 ```sh $ xmake run : run xmake run in 192.168.56.110:9091 .. hello world! : run command ok! ``` ## 远程重建工程 ```sh $ xmake -rv : run xmake -rv in 192.168.56.110:9091 .. [ 25%]: cache compiling.release src/main.cpp /usr/local/bin/ccache /usr/bin/xcrun -sdk macosx clang -c -Qunused-arguments -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp [ 50%]: linking.release test "/usr/bin/xcrun -sdk macosx clang++" -o build/macosx/x86_64/release/test build/.objs/test/macosx/x86_64/release/src/main.cpp.o -arch x86_64 -mmacosx-version-min=11.4 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -stdlib=libc++ -Wl,-x -lz [100%]: build ok! : run command ok! ``` ## 远程配置编译参数 ```sh $ xmake f --xxx --yy ``` ## 手动同步工程文件 连接的时候,会自动同步一次代码,后期代码改动,可以执行此命令来手动同步改动的文件。 ```sh $ xmake service --sync : sync files in 192.168.56.110:9091 .. Scanning files .. Comparing 3 files .. [+]: src/main.cpp [+]: .gitignore [+]: xmake.lua 3 files has been changed! Archiving files .. Uploading files with 1372 bytes .. : sync files ok! ``` ## 拉取远程文件 v2.7.1 版本我们新增了一个参数用于拉取远程指定的文件,通常我们可以用来完成构建后目标文件的拉取,将编译后的库文件下载到本地。 例如: ```sh xmake service --pull 'build/**' outputdir ``` 我们可以指定远程路径 `build/**` ,将所有匹配的文件全部拉取到本地 outputdir 目录下。 ## 断开远程连接 针对当前工程,断开连接,这仅仅影响当前工程,其他项目还是可以同时连接和编译。 ```sh $ xmake service --disconnect : disconnect 192.168.56.110:9091 .. : disconnected! ``` ## 查看服务器日志 ```sh $ xmake service --logs ``` ## 清理远程服务缓存和构建文件 我们也可以手动清理远程的任何缓存和构建生成的文件。 ```sh $ cd projectdir $ xmake service --clean ``` --- --- url: /zh/api/scripts/option-instance.md --- # 选项实例 {#option-instance} 此页面描述了 [配置选项](/zh/api/description/configuration-option) 的 `on_load()`、`on_check()` 或 `after_check()` 等函数的 `option` 接口。 在脚本域中,可以通过 `option` 参数来操作当前选项的各种属性和配置。 ## option:name * 获取选项的名字 #### 函数原型 ::: tip API ```lua option:name() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("test") on_check(function (option) print(option:name()) -- 输出: test end) ``` ## option:fullname * 获取选项的完整名字 #### 函数原型 ::: tip API ```lua option:fullname() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("mymod::test") on_check(function (option) print(option:fullname()) -- 输出: mymod::test end) ``` ## option:namespace * 获取选项的命名空间 #### 函数原型 ::: tip API ```lua option:namespace() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("mymod::test") on_check(function (option) print(option:namespace()) -- 输出: mymod end) ``` ## option:description * 获取选项的描述信息 #### 函数原型 ::: tip API ```lua option:description() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("test") set_description("This is a test option") on_check(function (option) print(option:description()) -- 输出: This is a test option end) ``` ## option:value * 获取选项的值 #### 函数原型 ::: tip API ```lua option:value() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("demo") set_default(true) after_check(function (option) local value = option:value() if value then print("demo option is enabled") else print("demo option is disabled") end end) ``` ## option:enabled * 检查选项是否已启用 #### 函数原型 ::: tip API ```lua option:enabled() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("test") after_check(function (option) if option:enabled() then print("Option is enabled") end end) ``` ## option:enable * 启用或禁用选项 #### 函数原型 ::: tip API ```lua option:enable(enable: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | enable | 是否启用 | #### 用法说明 ```lua option("float") after_check(function (option) if option:dep("micro"):enabled() then -- 如果micro选项启用,则禁用float选项 option:enable(false) end end) ``` ## option:set\_value * 设置选项的值 #### 函数原型 ::: tip API ```lua option:set_value(value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | value | 选项值 | #### 用法说明 ```lua option("test") on_check(function (option) -- 设置选项值为特定值 option:set_value("custom_value") end) ``` ## option:clear * 清除选项状态,需要重新检查 #### 函数原型 ::: tip API ```lua option:clear() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("test") after_check(function (option) -- 清除状态,下次构建时会重新检查 option:clear() end) ``` ## option:get * 获取选项在描述域的配置值 #### 函数原型 ::: tip API ```lua option:get(key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | #### 用法说明 任何在描述域的 `set_xxx` 和 `add_xxx` 配置值都可以通过这个接口获取到。 ```lua option("test") set_default(false) set_category("option") set_description("Test option") add_defines("TEST_MODE") add_links("testlib") on_check(function (option) -- 获取各种配置 local default = option:get("default") -- false local category = option:get("category") -- "option" local description = option:get("description") -- "Test option" local defines = option:get("defines") -- {"TEST_MODE"} local links = option:get("links") -- {"testlib"} -- 获取类型检查相关配置 local ctypes = option:get("ctypes") -- 获取C类型检查列表 local cfuncs = option:get("cfuncs") -- 获取C函数检查列表 local cincludes = option:get("cincludes") -- 获取C头文件检查列表 end) ``` ## option:set * 设置选项的配置值 #### 函数原型 ::: tip API ```lua option:set(key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | | value | 配置值 | #### 用法说明 如果你想添加值可以用 [option:add](#option-add)。 ```lua option("test") on_check(function (option) -- 设置链接库 option:set("links", "sdl2") -- 设置预定义宏 option:set("defines", "SDL_MAIN_HANDLED") -- 设置配置变量 option:set("configvar", option:name(), option:value(), {quote = false}) -- 设置编译标志 option:set("cxflags", "-O2", "-Wall") -- 设置头文件搜索路径 option:set("includedirs", "/usr/include/sdl2") -- 设置库文件搜索路径 option:set("linkdirs", "/usr/lib") end) ``` ::: tip 注意 任何脚本域下对 `option:set("xxx", ...)` 的配置,都是完全跟描述域的 `set_xxx` 接口保持一致的,具体参数说明,可以直接参考描述域下对应的 `set_xxx` 接口说明。 例如: * 描述域:`set_default(false)` * 脚本域:`option:set("default", false)` ::: ## option:add * 按名称添加到选项的值 #### 函数原型 ::: tip API ```lua option:add(key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | | value | 要添加的值 | #### 用法说明 ```lua option("test") on_check(function (option) -- 添加链接库 option:add("links", "sdl2", "pthread") -- 添加预定义宏 option:add("defines", "DEBUG", "VERSION=1") -- 添加编译标志 option:add("cxflags", "-g", "-O0") -- 添加头文件搜索路径 option:add("includedirs", "/usr/local/include") -- 添加库文件搜索路径 option:add("linkdirs", "/usr/local/lib") -- 添加系统链接库 option:add("syslinks", "dl", "m") -- 添加C类型检查 option:add("ctypes", "wchar_t") -- 添加C函数检查 option:add("cfuncs", "malloc", "free") -- 添加C头文件检查 option:add("cincludes", "stdio.h", "stdlib.h") end) ``` ::: tip 注意 任何脚本域下对 `option:add("xxx", ...)` 的配置,都是完全跟描述域的 `add_xxx` 接口保持一致的,具体参数说明,可以直接参考描述域下对应的 `add_xxx` 接口说明。 例如: * 描述域:`add_defines("DEBUG", "VERSION=1")` * 脚本域:`option:add("defines", "DEBUG", "VERSION=1")` ::: ## option:remove * 从选项中移除指定的值 #### 函数原型 ::: tip API ```lua option:remove(key: , value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | key | 配置键名 | | value | 要移除的值 | #### 用法说明 ```lua option("test") on_check(function (option) -- 移除特定的链接库 option:remove("links", "oldlib") -- 移除特定的预定义宏 option:remove("defines", "OLD_MACRO") -- 移除特定的编译标志 option:remove("cxflags", "-Wall") end) ``` ## option:deps * 获取选项的所有依赖 #### 函数原型 ::: tip API ```lua option:deps() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("info") add_deps("small", "micro") after_check(function (option) local deps = option:deps() if deps then for name, dep in pairs(deps) do print("Dependency:", name, "enabled:", dep:enabled()) end end end) ``` ## option:dep * 获取指定的依赖选项 #### 函数原型 ::: tip API ```lua option:dep(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 依赖名称 | #### 用法说明 ```lua option("float") add_deps("micro") after_check(function (option) local micro_dep = option:dep("micro") if micro_dep and micro_dep:enabled() then -- 如果micro依赖启用,则禁用当前选项 option:enable(false) end end) ``` ## option:orderdeps * 获取选项的顺序依赖 #### 函数原型 ::: tip API ```lua option:orderdeps() ``` ::: #### 参数说明 此函数不需要参数。 #### 用法说明 ```lua option("test") add_deps("dep1", "dep2") after_check(function (option) local orderdeps = option:orderdeps() if orderdeps then for i, dep in ipairs(orderdeps) do print("Order dependency", i, ":", dep:name()) end end end) ``` ## option:extraconf * 获取额外配置信息 #### 函数原型 ::: tip API ```lua option:extraconf(name: , key: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 配置名称 | | key | 配置键名 | #### 用法说明 ```lua option("test") add_csnippets("test_snippet", "int test() { return 1; }", {output = true}) on_check(function (option) -- 检查snippet是否配置了output local has_output = option:extraconf("csnippets", "test_snippet", "output") if has_output then print("test_snippet has output configuration") end end) ``` ::: tip 提示 * 在 `on_check`、`after_check` 等脚本函数中,`option` 参数代表当前正在处理的选项实例 * 可以通过 `option:get()` 获取描述域中设置的各种配置 * 可以通过 `option:set()` 和 `option:add()` 动态修改选项配置 * 使用 `option:dep()` 可以访问依赖选项,实现复杂的选项逻辑 * 选项的启用/禁用状态可以通过 `option:enabled()` 和 `option:enable()` 来控制 ::: --- --- url: /zh/guide/project-configuration/configure-targets.md --- # 配置目标 {#configure-targets} 当创建完一个空工程后,我们会得到如下一个最为基础的配置文件。 ```lua [xmake.lua] add_rules("mode.debug", "mode.release") target("test") set_kind("binary") add_files("src/*.cpp") ``` 其中,`mode.release` 是默认的编译模式规则,它会为目标构建自动添加一些常规的优化编译选项,例如:`-O2` 等等。 我们也可以通过 `xmake f -m debug` 切换到 `mode.debug` 调试模式,自动配置上一些调试选项,例如:`-g` 等等。 当然,我们也可以不去配置模式规则,完全自己去配置它们。 ```lua [xmake.lua] target("test") set_kind("binary") add_files("src/*.cpp") ``` ## 配置宏定义 {#configure-defines} 我们可以通过 [add\_defines](/zh/api/description/project-target#add-defines) 为目标程序添加一个宏定义选项,例如: ```lua [xmake.lua] target("test") set_kind("binary") add_files("src/*.cpp") add_defines("DEBUG") ``` 在 test 目标作用域下,我们对其配置了一个 `-DDEBUG` 的宏定义编译选项,它只会对 test 这一个目标生效。 我们也可以通过 `xmake -v` 命令去快速验证添加的配置是否生效。 ```sh [ 23%]: cache compiling.release src/main.cpp /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -c -Q unused-arguments -target x86_64-apple-macos15.2 -isysroot /Applications/Xcode.app/Contents/Develop er/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -fvisibility=hidden -fvisibility-inline s-hidden -O3 -DNDEBUG -o build/.objs/test/macosx/x86_64/release/src/main.cpp.o src/main.cpp ``` 可以看到,`-DDEBUG` 已经被添加到了 clang 编译命令中。 ## 同时配置多个目标 {#configure-multi-targets} 如果想要让多个编译目标同时生效,我们可以将它移到全局根作用域。 ```lua [xmake.lua] add_defines("DEBUG") target("foo") set_kind("binary") add_files("src/*.cpp") target("bar") set_kind("binary") add_files("src/*.cpp") ``` 这个时候,foo 和 bar 两个目标程序都会带上 `-DDEBUG` 编译选项。 根作用域的配置,它会影响当前 xmake.lua 配置文件,以及所有子 xmake.lua 文件中的目标程序(也就是被 includes 引入的子目录配置)。 ```lua [xmake.lua] add_defines("DEBUG") target("foo") set_kind("binary") add_files("src/*.cpp") includes("bar") ``` ```lua [bar/xmake.lua] target("bar") set_kind("binary") add_files("src/*.cpp") ``` 如果 bar 目标在 `bar/xmake.lua` 子配置文件中,也会被生效。 ::: tip 注意 但是子文件中的根作用域配置无法影响平级,父级配置文件中的目标。 ::: ```lua [xmake.lua] target("foo") set_kind("binary") add_files("src/*.cpp") includes("bar") ``` ```lua [bar/xmake.lua] add_defines("DEBUG") target("bar") set_kind("binary") add_files("src/*.cpp") target("zoo") set_kind("binary") add_files("src/*.cpp") ``` 这里,尽管 `add_defines` 被配置到根作用域,但它仅仅影响 bar 和 zoo 两个目标,无法影响到 foo 目标。 也就是说,根作用域的影响范围是按树状结构,一层层生效下去的。 ## 配置优化选项 {#configure-optimization} `mode.release` 会自动引入一些优化选项,而我们也可以自己通过 [set\_optimize](/zh/api/description/project-target#set-optimize) 接口去配置它。 例如: ```lua set_optimize("faster") ``` 就会为目标配置上 `-O2` 编译选项。 ::: tip 注意 不同的编译器的优化选项是不同的,xmake 会自动映射到最为合适的编译选项。 ::: 更多详情,请参看文档:[set\_optimize](/zh/api/description/project-target#set-optimize)。 ## 配置头文件搜索目录 {#configure-includedirs} ```lua add_includedirs("/tmp") ``` 它会添加 `-I/tmp` 编译选项给编译器。 更多详情,请参看文档:[add\_includedirs](/zh/api/description/project-target#add-includedirs)。 ## 配置链接库搜索目录 {#configure-linkdirs} ```lua add_linkdirs("/tmp") ``` 它会添加 `-L/tmp` 链接选项给链接器。 更多详情,请参看文档:[add\_linkdirs](/zh/api/description/project-target#add-linkdirs)。 ## 配置链接库 {#configure-links} ```lua add_links("foo") ``` 它会添加 `-lfoo` 链接选项给链接器,这通常需要搭配 [add\_linkdirs](/zh/api/description/project-target#add-linkdirs) 来使用。 ```lua add_links("foo") add_linkdirs("/tmp") ``` 更多详情,请参看文档:[add\_links](/zh/api/description/project-target#add-links)。 ## 配置系统链接库 `add_links` 通常用于链接用户生成的库,而 `add_syslinks` 可以添加系统库,不需要额外的 `add_linkdirs`。 并且它的链接顺序也相对靠后。 ```lua add_links("foo") add_syslinks("pthread", "dl") ``` 如果同时配置了 links 和 syslinks,它的链接顺序如下: ```sh -lfoo -lpthread -ldl ``` ## 配置原始编译选项 {#configure-flags} 上述提到的 `add_defines` 等接口,都属于抽象 API,由于大部分编译器都支持,因此 Xmake 对它们做了抽象,使用户能够更加方便的使用,并且能够提供更好的跨平台,跨编译器。 然而,我们也能够通过 `add_cxxflags` 接口,为 C++ 代码添加特定的编译选项,比如: ```lua add_cxflags("-DDEBUG") ``` 它跟 `add_defines("DEBUG")` 效果是等价的,但是 `add_defines` 更加的通用,适用于所有的编译器, 而 `add_cxxflags("-DDEBUG")` 可能仅仅适用于某几个编译器,因为不是所有的编译器都是通过 `-D` 来定义宏的。 另外,我们也可以通过 `add_cflags` 接口,为 C 代码添加编译选项,以及 `add_cxflags` 同时为 C/C++ 代码添加编译选项。 更多详情,请参考文档: * [add\_cflags](/zh/api/description/project-target#add-cflags) * [add\_cxflags](/zh/api/description/project-target#add-cxflags) * [add\_cxxflags](/zh/api/description/project-target#add-cxxflags) ## 目标类型配置 {#configure-target-types} ### 设置目标类型 通过 `set_kind()` 可以设置不同的目标类型: ```lua target("app") set_kind("binary") -- 可执行程序 add_files("src/*.cpp") target("lib") set_kind("static") -- 静态库 add_files("src/*.cpp") target("dll") set_kind("shared") -- 动态库 add_files("src/*.cpp") target("obj") set_kind("object") -- 对象文件集合 add_files("src/*.cpp") target("header") set_kind("headeronly") -- 纯头文件库 add_headerfiles("include/*.h") ``` ### 目标类型说明 | 类型 | 描述 | 输出文件 | |------|------|----------| | binary | 可执行程序 | app.exe, app | | static | 静态库 | libapp.a, app.lib | | shared | 动态库 | libapp.so, app.dll | | object | 对象文件集合 | \*.o, \*.obj | | headeronly | 纯头文件库 | 无编译输出 | | phony | 虚拟目标 | 无输出,仅用于依赖管理 | ### 虚拟目标 (phony) 虚拟目标不生成实际文件,仅用于管理依赖关系: ```lua target("test1") set_kind("binary") add_files("src/test1.cpp") target("test2") set_kind("binary") add_files("src/test2.cpp") target("all-tests") set_kind("phony") add_deps("test1", "test2") ``` 执行 `xmake build all-tests` 会同时构建 test1 和 test2。 ## 配置目标依赖 {#configure-targetdeps} 我们可以通过 [add\_deps](/zh/api/description/project-target#add-deps) 接口来配置两个目标程序间的依赖。 这通常可用于一个可执行程序依赖一个静态库(或者动态库)的场景,例如: ```lua target("foo") set_kind("static") add_files("src/foo.cpp") target("test") set_kind("binary") add_deps("foo") add_files("src/main.cpp") ``` 由于 test 和 foo 库程序之间通过 `add_deps` 配置了依赖关系,当我们编译 test 的时候,会自动编译 foo 依赖库,并且也会自动的去链接它,而不再需要额外的 `add_links` 和 `add_linkdirs` 配置。 更多关于依赖的配置说明,请参考文档:[add\_deps](/zh/api/description/project-target#add-deps)。 ## 目标文件配置 {#configure-target-files} ### 添加源文件 ```lua target("app") add_files("src/*.cpp") -- 添加所有 cpp 文件 add_files("src/*.c") -- 添加所有 c 文件 add_files("src/*.asm") -- 添加汇编文件 add_files("src/*.m") -- 添加 Objective-C 文件 add_files("src/*.mm") -- 添加 Objective-C++ 文件 ``` ### 排除特定文件 ```lua target("app") add_files("src/*.cpp|test.cpp") -- 排除src目录下测试文件 ``` ### 添加头文件 ```lua target("header-lib") set_kind("headeronly") add_headerfiles("include/*.h") -- 添加头文件 add_headerfiles("include/*.hpp") -- 添加 C++ 头文件 add_headerfiles("include/*.h", {install = false}) -- 不安装的头文件 ``` ### 添加安装文件 ```lua target("app") add_installfiles("assets/*.png", {prefixdir = "share/app"}) -- 安装资源文件 add_installfiles("config/*.conf", {prefixdir = "etc"}) -- 安装配置文件 ``` ## 目标属性配置 {#configure-target-properties} ### 设置目标文件名 ```lua target("app") set_basename("myapp") -- 生成的文件名为 myapp.exe ``` ### 设置目标目录 ```lua target("app") set_targetdir("bin") -- 输出到 bin 目录 ``` ### 设置安装目录 ```lua target("app") set_installdir("bin") -- 安装到 bin 目录 ``` ### 设置版本信息 ```lua target("app") set_version("1.0.0") -- 设置版本号 ``` ## 目标可见性配置 {#configure-target-visibility} ### 设置符号可见性 ```lua target("lib") set_kind("shared") set_symbols("hidden") -- 隐藏符号,减少导出表大小 ``` ## 目标优化配置 {#configure-target-optimization} ### 设置优化级别 ```lua target("app") set_optimize("fast") -- 快速优化 set_optimize("faster") -- 更快速优化 set_optimize("fastest") -- 最快优化 set_optimize("smallest") -- 大小优化 set_optimize("none") -- 无优化 ``` ### 设置调试信息 ```lua target("app") set_symbols("debug") -- 添加调试符号 set_strip("debug") -- 链接时去除调试符号 set_strip("all") -- 链接时去除所有符号 ``` ## 目标语言配置 {#configure-target-languages} ### 设置语言标准 ```lua target("app") set_languages("c++17") -- 设置 C++ 标准 set_languages("c11") -- 设置 C 标准 ``` ### 设置语言特性 ```lua target("app") set_languages("c++17", "c11") -- 同时支持 C++17 和 C11 ``` ## 目标平台配置 {#configure-target-platforms} ### 设置目标平台 ```lua target("app") set_plat("android") -- 设置为 Android 平台 set_arch("arm64-v8a") -- 设置为 ARM64 架构 ``` ### 条件配置 ```lua target("app") if is_plat("windows") then add_defines("WIN32") add_links("user32") elseif is_plat("linux") then add_defines("LINUX") add_links("pthread") end ``` ## 目标选项配置 {#configure-target-options} ### 关联选项 ```lua option("enable_gui") set_default(false) set_description("Enable GUI support") target("app") set_options("enable_gui") -- 关联选项 ``` ### 条件选项 ```lua target("app") if has_config("enable_gui") then add_defines("GUI_ENABLED") add_links("gtk+-3.0") end ``` ## 目标规则配置 {#configure-target-rules} ### 添加构建规则 ```lua target("app") add_rules("mode.debug", "mode.release") -- 添加调试和发布模式 add_rules("qt.widgetapp") -- 添加 Qt 应用规则 add_rules("wdk.driver") -- 添加 WDK 驱动规则 ``` ### 自定义规则 ```lua rule("myrule") set_extensions(".my") on_build_file(function (target, sourcefile, opt) -- 自定义构建逻辑 end) target("app") add_rules("myrule") -- 应用自定义规则 ``` ## 目标运行时配置 {#configure-target-runtime} ### 设置运行时库 ```lua target("app") set_runtimes("MT") -- 静态运行时 (MSVC) set_runtimes("MD") -- 动态运行时 (MSVC) ``` ### 设置运行时路径 ```lua target("app") set_runtimes("MD") add_rpathdirs("$ORIGIN") -- 设置相对路径查找 ``` ## 目标工具链配置 {#configure-target-toolchain} ### 设置工具链 ```lua target("app") set_toolset("clang") -- 使用 Clang 工具链 set_toolset("gcc") -- 使用 GCC 工具链 set_toolset("msvc") -- 使用 MSVC 工具链 ``` ### 设置编译器 ```lua target("app") set_toolset("cc", "clang") -- 设置 C 编译器 set_toolset("cxx", "clang++") -- 设置 C++ 编译器 ``` ## 目标分组配置 {#configure-target-groups} ### 设置目标分组 ```lua target("app") set_group("apps") -- 设置分组为 apps target("lib") set_group("libs") -- 设置分组为 libs target("test") set_group("tests") -- 设置分组为 tests ``` ## 目标默认配置 {#configure-target-defaults} ### 设置默认目标 ```lua target("app") set_default(true) -- 设为默认构建目标 target("test") set_default(false) -- 不设为默认构建目标 ``` ### 启用/禁用目标 ```lua target("app") set_enabled(true) -- 启用目标 target("old") set_enabled(false) -- 禁用目标 ``` ## 更多信息 {#more-information} * 完整的 API 文档:[工程目标 API](/zh/api/description/project-target) * 目标实例接口:[目标实例 API](/zh/api/scripts/target-instance) * 内置规则参考:[内置规则](/zh/api/description/builtin-rules) --- --- url: /zh/api/description/configuration-option.md --- # 配置选项 定义和设置选项开关,每个`option`对应一个选项,可用于自定义编译配置选项、开关设置。 :::tip 注意 除了`target`以外的所有域接口,例如`option`,`task`等的接口,默认不能放置在外面的全局作用域中的(除非部分跟target共用的接口除外)。 如果要设置值影响所有`option`,`task`等选项,可以通过匿名全局域来设置。 ::: 例如: ```lua -- 进入option的匿名全局域,里面的设置会同时影响test和test2选项 option() add_defines("DEBUG") option("test") -- ... -- 尽量保持缩进,因为这个之后的所有设置,都是针对test选项的 option("test2") -- ... ``` :::tip 注意 `option`域是可以重复进入来实现分离设置的,如果要显示离开当前选项的作用域设置,可以手动调用[option\_end](#option-end)接口。 ::: ## option ### 定义选项 #### 函数原型 ::: tip API ```lua option(name: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 选项名称字符串 | #### 用法说明 定义和设置选项开关,可用于自定义编译配置选项、开关设置。 例如,定义一个是否启用test的选项: ```lua option("test") set_default(false) add_defines("TEST") ``` 然后关联到指定的target中去: ```lua target("demo") add_options("test") ``` 这样,一个选项就算定义好了,如果这个选项被启用,那么编译这个target的时候,就会自动加上`-DTEST`的宏定义。 ```lua -- 手动启用这个选项 $ xmake f --test=y $ xmake ``` ## option\_end ### 结束定义选项 #### 函数原型 ::: tip API ```lua option_end() ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | - | 无参数 | #### 用法说明 这是一个可选api,显示离开选项作用域,用法和[target\_end](/zh/api/description/project-target#target_end)类似。 ## add\_deps ### 添加选项依赖 #### 函数原型 ::: tip API ```lua add_deps(deps: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | deps | 依赖选项名称字符串或数组 | | ... | 可变参数,可传递多个依赖名称 | #### 用法说明 通过设置依赖,可以调整选项的检测顺序,一般用于[on\_check](#on_check)等检测脚本的调用时机。 ```lua option("small") set_default(true) on_check(function (option) -- ... end) option("test") add_deps("small") set_default(true) after_check(function (option) if option:dep("small"):enabled() then option:enable(false) end end) ``` 当依赖的small选项检测完成后,通过判断small选项的状态,来控制test的选项状态。 :::tip 注意 由于 on\_check 只有在没有设置 default 值的情况下才会被执行,因此如果设置了 default 值,那么可以在 after\_check 阶段处理自定义逻辑。 ::: ## before\_check ### 选项检测之前执行此脚本 #### 函数原型 ::: tip API ```lua before_check(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 检测前脚本函数,参数为option | #### 用法说明 ```lua option("zlib") before_check(function (option) end) ``` ## on\_check ### 自定义选项检测脚本 #### 函数原型 ::: tip API ```lua on_check(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 检测脚本函数,参数为option | #### 用法说明 此脚本会覆盖内置的选项检测逻辑。 ```lua option("test") add_deps("small") on_check(function (option) option:enable(true) end) ``` :::tip 注意 仅仅在 `set_default` 没有被设置的情况下,才会执行 `on_check` 进行自定义的选项检测脚本。 ::: ## after\_check ### 选项检测之后执行此脚本 #### 函数原型 ::: tip API ```lua after_check(script: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | script | 检测后脚本函数,参数为option | #### 用法说明 在选项检测完成后,执行此脚本做一些后期处理,也可以在此时重新禁用选项: ```lua option("test") add_deps("small") add_links("pthread") after_check(function (option) option:enable(false) end) ``` ## set\_values ### 设置选项值列表 #### 函数原型 ::: tip API ```lua set_values(values: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | values | 选项值字符串或数组 | | ... | 可变参数,可传递多个值 | #### 用法说明 仅用于`xmake f --menu`的图形菜单配置时,提供选项值列表供用户快速选择使用,例如: ```lua option("test") set_default("b") set_values("a", "b", "c") ``` 效果图如下: ## set\_default ### 设置选项默认值 #### 函数原型 ::: tip API ```lua set_default(value: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | value | 默认值(字符串、布尔值或数字) | #### 用法说明 在没有通过`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`值的选项,可以通过[has\_config](/zh/api/description/conditions#has-config)来进行判断,选项是否被启用。 可以通过[get\_config](/zh/api/description/global-interfaces#get-config)和[has\_config](/zh/api/description/conditions#has-config)接口来获取定义的选项状态,也就是用户通过`xmake f --option1=xxx`设置的状态值。 如果是`string`类型的选项,可以在内建变量中直接使用,例如: ```lua -- 定义一个路径配置选项,默认使用临时目录 option("rootdir") set_default("$(tmpdir)") target("test") -- 添加指定选项目录中的源文件 add_files("$(rootdir)/*.c") ``` 其中,`$(rootdir)` 就是自定义的选项内建变量,通过手动配置,可以动态修改它的值: ```sh $ xmake f --rootdir=~/projectdir/src $ xmake ``` 给这个`rootdir`选项指定一个其他的源码目录路径,然后编译。 选项的检测行为: | default值 | 检测行为 | | ---------- | --------------------------------------------------------------------------------------------- | | 没有设置 | 优先手动配置修改,默认禁用,否则会启用自动检测,也会执行 on\_check,可根据手动传入的值类型,自动切换boolean和string类型 | | false | 开关选项,不自动检测,默认禁用,可手动配置修改 | | true | 开关选项,不自动检测,默认启用,可手动配置修改 | | string类型 | 无开关状态,不自动检测,可手动配置修改,一般用于配置变量传递 | ## set\_showmenu ### 设置是否启用菜单显示 #### 函数原型 ::: tip API ```lua set_showmenu(showmenu: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | showmenu | 是否显示菜单(布尔值) | #### 用法说明 如果设置为`true`,那么在`xmake f --help`里面就会出现这个选项,也就能通过`xmake f --optionname=xxx`进行配置,否则只能在`xmake.lua`内部使用,无法手动配置修改。 ```lua option("test") set_showmenu(true) ``` 设置为启用菜单后,执行`xmake f --help`可以看到,帮助菜单里面多了一项: ``` Options: ... --test=TEST ``` :::tip 注意 2.6.8 之后,此选项默认改为启用,通常都不需要额外配置它。 ::: ## set\_category ### 设置选项分类,仅用于菜单显示 #### 函数原型 ::: tip API ```lua set_category(category: ) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | category | 分类名称字符串 | #### 用法说明 这个是个可选配置,仅用于在帮助菜单中,进行分类显示选项,同一类别的选项,会在同一个分组里面显示,这样菜单看起来更加的美观。 例如: ```lua option("test1") set_category("test") option("test2") set_category("test") option("demo1") set_category("demo") option("demo2") set_category("demo") ``` 这里四个选项分别归类于两个分组:`test`和`demo`,那么显示的布局类似这样: ```sh 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_category("root menu/test1") -- 'choice' option with values: "a", "b", "c" option("test2") set_default("a") set_values("a", "b", "c") set_category("root menu/test2") -- 'string' option option("test3") set_default("xx") set_category("root menu/test3/test3") -- 'number' option option("test4") set_default(6) set_category("root menu/test4") ``` 上述配置最后显示的菜单界面路径结构: * root menu * test1 * test2 * test3 * test3 * test4 效果图如下: ## set\_description ### 设置菜单显示描述 #### 函数原型 ::: tip API ```lua set_description(description: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | description | 描述信息字符串或数组 | | ... | 可变参数,可传递多行描述 | #### 用法说明 设置选项菜单显示时,右边的描述信息,用于帮助用户更加清楚的知道这个选项的用途,例如: ```lua option("test") set_default(false) set_description("Enable or disable test") ``` 生成的菜单内容如下: ``` Options: ... --test=TEST Enable or disable test (default: false) ``` 这个接口也支持多行显示,输出更加详细的描述信息,例如: ```lua option("mode") set_default("debug") set_description("Set build mode", " - debug", " - release", " - profile") ``` 生成的菜单内容如下: ``` Options: ... --mode=MODE Set build mode (default: debug) - debug - release - profile ``` 看到这个菜单,用户就能清楚地知道,定义的这个`mode`选项的具体用处,以及如何使用了: ```sh $ xmake f --mode=release ``` ## add\_links ### 添加链接库检测 #### 函数原型 ::: tip API ```lua add_links(links: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | links | 链接库名称字符串或数组 | | ... | 可变参数,可传递多个库名称 | #### 用法说明 如果指定的链接库检测通过,此选项将被启用,并且对应关联的target会自动加上此链接,例如: ```lua option("pthread") add_links("pthread") add_linkdirs("/usr/local/lib") target("test") add_options("pthread") ``` 如果检测通过,`test`目标编译的时候就会自动加上:`-L/usr/local/lib -lpthread` 编译选项 ## add\_linkdirs ### 添加链接库检测时候需要的搜索目录 #### 函数原型 ::: tip API ```lua add_linkdirs(linkdirs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | linkdirs | 链接目录路径字符串或数组 | | ... | 可变参数,可传递多个目录路径 | #### 用法说明 这个是可选的,一般系统库不需要加这个,也能检测通过,如果确实没找到,可以自己追加搜索目录,提高检测通过率。具体使用见:[add\_links](#add_links) ## add\_rpathdirs ### 添加程序运行时动态库的加载搜索目录 #### 函数原型 ::: tip API ```lua add_rpathdirs(rpathdirs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | rpathdirs | rpath目录路径字符串或数组 | | ... | 可变参数,可传递多个目录路径 | #### 用法说明 在选项通过检测后,会自动添加到对应的target上去,具体使用见:[target:add\_rpathdirs](/zh/api/description/project-target#add-rpathdirs)。 ## add\_cincludes ### 添加c头文件检测 #### 函数原型 ::: tip API ```lua add_cincludes(includes: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 头文件名称字符串或数组 | | ... | 可变参数,可传递多个头文件名称 | #### 用法说明 如果c头文件检测通过,此选项将被启用,例如: ```lua option("pthread") 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](#on_check)中去实现。 ## add\_cxxincludes ### 添加c++头文件检测 #### 函数原型 ::: tip API ```lua add_cxxincludes(includes: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | includes | 头文件名称字符串或数组 | | ... | 可变参数,可传递多个头文件名称 | #### 用法说明 与[add\_cincludes](#add_cincludes)类似,只是检测的头文件类型是c++头文件。 ## add\_ctypes ### 添加c类型检测 #### 函数原型 ::: tip API ```lua add_ctypes(types: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 类型名称字符串或数组 | | ... | 可变参数,可传递多个类型名称 | #### 用法说明 如果c类型检测通过,此选项将被启用,例如: ```lua option("wchar") add_ctypes("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](#on_check)中去实现。 ## add\_cxxtypes ### 添加c++类型检测 #### 函数原型 ::: tip API ```lua add_cxxtypes(types: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | types | 类型名称字符串或数组 | | ... | 可变参数,可传递多个类型名称 | #### 用法说明 与[add\_ctypes](#add_ctypes)类似,只是检测的类型是c++类型。 ## add\_csnippets ### 添加c代码片段检测 #### 函数原型 ::: tip API ```lua add_csnippets(name: , code: , { tryrun = , output = , number = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 代码片段名称字符串 | | code | 代码片段字符串 | | tryrun | 是否尝试运行检测 | | output | 是否捕获输出 | | number | 是否输出数字 | #### 用法说明 如果现有的[add\_ctypes](#add_ctypes), [add\_cfuncs](#add_cfuncs)等不能满足当前的检测需求, 可以用这个接口实现更加定制化检测一些编译器特性检测,具体见: [add\_cxxsnippets](#add_cxxsnippets)。 ## add\_cxxsnippets ### 添加c++代码片段检测 #### 函数原型 ::: tip API ```lua add_cxxsnippets(name: , code: , { tryrun = , output = , number = }) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | name | 代码片段名称字符串 | | code | 代码片段字符串 | | tryrun | 是否尝试运行检测 | | output | 是否捕获输出 | | number | 是否输出数字 | #### 用法说明 可以用这个接口实现更加定制化检测一些编译器特性检测,尤其是c++的各种特性的检测支持,例如: ```lua option("constexpr") add_cxxsnippets("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](#on_check)中去实现。 v2.5.7 版本后,新增 `{tryrun = true}` 和 `{output = true}` 两个选项用于尝试运行检测和捕获输出。 设置 tryrun 可以尝试运行来检测: ```lua option("test") add_cxxsnippets("HAS_INT_4", "return (sizeof(int) == 4)? 0 : -1;", {tryrun = true}) ``` 设置 output 也会尝试去检测,并且额外捕获运行的输出内容。 ```lua option("test") add_cxxsnippets("INT_SIZE", 'printf("%d", sizeof(int)); return 0;', {output = true, number = true}) ``` :::tip 注意 设置为捕获输出,当前 option 不能再设置其他 snippets ::: 我们也可以通过 `is_config` 获取绑定到option的输出。 ```lua if is_config("test", "8") then -- xxx end ``` ## add\_cfuncs ### 添加c库函数检测 #### 函数原型 ::: tip API ```lua add_cfuncs(funcs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | funcs | 函数名称字符串或数组 | | ... | 可变参数,可传递多个函数名称 | #### 用法说明 ```lua option("setjmp") add_cincludes("setjmp.h") add_cfuncs("sigsetjmp", "setjmp") add_defines("HAVE_SETJMP") target("test") add_options("setjmp") ``` 此选项检测是否存在`setjmp`的一些接口,如果检测通过那么`test` 目标程序将会加上 `HAVE_SETJMP` 的宏定义。 里面的函数片段支持下面几种语法格式: ```lua -- 单纯检测函数地址是否存在,内部会尝试去它的地址来判断 sigsetjmp -- 如果有些函数是宏定义 wrap 的,可以通过这种方式绕通过检测 sigsetjmp((void*)0, 0) -- 也可以指定一个完整函数语句,例如:funcname{codebody} sigsetjmp{sigsetjmp((void*)0, 0);} sigsetjmp{int a = 0; sigsetjmp((void*)a, a);} ``` :::tip NOTE 注意,检测的函数通常需要附带 `add_cincludes` 确保函数能够被正常 include 进来,否则检测会失败。 ::: ## add\_cxxfuncs ### 添加c++库函数检测 #### 函数原型 ::: tip API ```lua add_cxxfuncs(funcs: , ...) ``` ::: #### 参数说明 | 参数 | 描述 | |------|------| | funcs | 函数名称字符串或数组 | | ... | 可变参数,可传递多个函数名称 | #### 用法说明 用法跟 [add\_cfuncs](#add_cxxfuncs) 一致。 --- --- url: /zh/posts/switch-library-kind.md --- 如果你想在同一个target上既编译静态库,又能编译动态库,那么稍微修改下 xmale.lua就行了: ```lua add_target("test") -- 设置编译target的类型,之前是:static/shared,现在改成动态的 set_kind("$(kind)") -- 添加文件 add_files(*.c) ``` 好了,现在默认编译的时候,会生成静态库:libtest.a 如果你想生成动态库,只需要执行: ```bash # 简写 xmake f -k shared # 或者 xmake config --kind=shared # 编译 xmake ``` 配置成动态库模式,重建下就行了。。参数:`-k/--kind` 可以手动在配置的时候指定,需要编译的target类型,实际会去影响: ```lua set_kind("$(kind)") ``` 中的$(kind)的配置变量。。 如果你想在target针对static/shared类型,分别处理不同的宏开关,也可以使用 `if kinds("static") then ` 来判断 ```lua add_target("test") -- 设置编译target的类型,之前是:static/shared,现在改成动态的 set_kind("$(kind)") -- 添加文件 add_files(*.c) -- 如果是动态库,则定义宏:SHARED if kinds("shared") then add_defines("SHARED") end ``` 上面说的 `-k/--kind`配置参数,是xmake内置参数,影响当前工程的所有指定了 `$(kind)` 的target,如果觉得这样影响面太广,我想让不同的target,在不同的时机切换编译静态库和动态库,可以使用option的接口,自定义一个配置参数: ```lua -- 自定义一个配置参数 add_option("mykind") set_option_showmenu(true) set_option_description("Set my target kind: static or shared") add_target("test") -- 设置编译target的类型,使用 xmake --mykind=static参数 set_kind("$(mykind)") -- 添加文件 add_files(*.c) ``` 这样,只需要执行`xmake f --mykind=shared` 就可以使用自己的配置参数,进行切换编译类型了。。 --- --- url: /zh/posts/custom-task.md --- task是xmake 2.0开始新增的特性,也是插件开发的核心,在 [插件开发之hello xmake](https://xmake.io/zh/) 中我们简单介绍了下task的定义和使用 当然task不仅可以用来写插件,而且还可以写一些简单的自定义任务。。 我们先看下一个简单task实现: ```lua -- 定义一个名叫hello的task任务 task("hello") -- task运行的入口 on_run(function () -- 显示hello xmake! print("hello xmake!") end) ``` 这是一个最简单的task,相比插件task,它少了对 `set_menu` 的设置,当然你也可以加上,好、这样就个可以在命令行中调用它。。 而这个hello task没有设置set\_menu,那么只能在自定义脚本里面调用了。。 ```lua target("demo") -- 自定义clean action on_clean(function(target) -- 导入task模块 import("core.project.task") -- 运行这个hello task task.run("hello") end) ``` 如果想要增加参数传递,有两种方式: 1. 通过`set_menu`添加一个命令行的选项菜单,通过option模块来访问参数(支持命令行、脚本传参) 2. 直接通过脚本传参 我们先看下第二种比较简单,不需要定义命令行菜单,只需要task定义和调用处双方约定好参数规则就行: ```lua -- 直接传参,{} 这个是给第一种选项传参使用,这里置空 -- 这里在最后面传入了两个参数:arg1, arg2 task.run("hello", {}, "arg1", "arg2") ``` 那如何获取这两个参数呢? ```lua -- 定义一个名叫hello的task任务 task("hello") -- task运行的入口,定义为两个参数 on_run(function (arg1, arg2) -- 显示hello xmake! print("hello xmake: %s %s!", arg1, arg2) end) ``` 怎么样简单吧,当然这种传参方式没法通过命令行进行外部传参,所以一般用于一些内置的task间调用,像插件这种高级task,就需要第一种传参方式了 这个详情请参考:[插件开发之参数配置](https://xmake.io/zh/) --- --- url: /zh/posts/custom-action.md --- xmake提供了自定义打包、安装、运行脚本,可以更加灵活的针对个人实际需求来操作xmake 这里用一个例子详细说明下,比如有个需求,我需要自动编译、安装、运行android app工程,并且能够支持jni 可以进行如下操作 首先创建个基于ant的android app工程,目录结构如下: ``` app └── android ├── AndroidManifest.xml ├── ant.properties ├── bin │   └── Demo-debug.apk ├── build.xml ├── jni │   └── demo.c ├── libs │   └── armeabi │   └── libdemo.so ├── local.properties ├── proguard-project.txt ├── project.properties ├── res │   ├── drawable-hdpi │   │   └── ic_launcher.png │   ├── drawable-ldpi │   │   └── ic_launcher.png │   ├── drawable-mdpi │   │   └── ic_launcher.png │   ├── drawable-xhdpi │   │   └── ic_launcher.png │   ├── layout │   │   └── main.xml │   └── values │   └── strings.xml ├── src │   └── com │   └── demo │   └── DemoTest.java └── xmake.lua ``` 新版本中对自定义脚本进行了重大升级,支持了task机制,以及类库import机制,写法上也更加的精简可读 我们可以对比下新老版本的自定义脚本写法,当然新版的xmake对这些老的api也是向下兼容的,如果还在使用老版本api,也是不影响使用的。。 我们重点讲解下新版的写法: ```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) -- trace print("buiding app") -- 使用ant编译app成apk文件,输出信息重定向到日志文件 os.run("ant debug") end) -- 设置自定义安装脚本,自动安装apk文件 on_install(function (target) -- trace print("installing app") -- 使用adb安装打包生成的apk文件 os.run("adb install -r ./bin/Demo-debug.apk") end) -- 设置自定义运行脚本,自动运行安装好的app程序,并且自动获取设备输出信息 on_run(function (target) -- run it os.run("adb shell am start -n com.demo/com.demo.DemoTest") os.run("adb logcat") end) ``` 修改完xmake.lua后,就可以很方便的使用了: ```bash # 重新编译工程,生成libdemo.so到app/libs/armeabi xmake -r # 打包app为apk xmake p # 安装apk到设备上 xmake i # 运行app,并获取日志信息 xmake r demo ``` 如果觉得上面的步骤有点繁琐,可以简化成: ```bash # 安装的时候,会先去自动打包,所以可以省略xmake p xmake -r; xmake i; xmake r demo ``` 如果是增量编译,不需要重建,可以继续简化: ```bash xmake i; xmake r demo ``` 当然,由于是根据自己的实际需求自定义的脚本,可能跨平台性有点弱,像这里只能支持android的编译平台, 我们继续重点说下新版本中这些的api的使用,xmake针对 构建、打包、清除、安装、卸载、运行都提供了对应的自定义脚本入口 下面的on\_xxx接口会直接替换内置的实现 * on\_build: 自定义构建脚本 * on\_clean: 自定义清除脚本 * on\_package: 自定义打包脚本 * on\_install: 自定义安装脚本 * on\_uninstall: 自定义卸载脚本 * on\_run: 自定义运行脚本 下面的 before\_xxx接口,会在on\_xxx之前执行 * before\_build: 在构建之前执行一些自定义脚本 * before\_clean: 在清除之前执行一些自定义脚本 * before\_package: 在打包之前执行一些自定义脚本 * before\_install: 在安装之前执行一些自定义脚本 * before\_uninstall: 在卸载之前执行一些自定义脚本 * before\_run: 在运行之前执行一些自定义脚本 下面的 after\_xxx接口,会在on\_xxx之后执行 * after\_build: 在构建之后执行一些自定义脚本 * after\_clean: 在清除之后执行一些自定义脚本 * after\_package: 在打包之后执行一些自定义脚本 * after\_install: 在安装之后执行一些自定义脚本 * after\_uninstall: 在卸载之后执行一些自定义脚本 * after\_run: 在运行之后执行一些自定义脚本 这些api的原型都是: ```lua function (target) end ``` 其中的参数就是当前的target,你可以从中获取一些基本信息,例如: ```lua on_run(function (target) -- 显示目标名 print(target:name()) -- 显示目标文件路径 print(target:targetfile()) -- 显示目标的构建类型 print(target:get("kind")) -- 显示目标的宏定义 print(target:get("defines")) -- 其他通过 set_/add_接口设置的target信息,都可以通过 target:get("xxx") 来获取 end) ``` 自定义脚本中,其作用域和xmake.lua上层的描述域是不同的,xmake里面有严格的沙盒管理,不会导致互相冲突 而且自定义脚本内部提供了大量内建类库和扩展类库,以供使用,扩展类库可以通过 [import](https://xmake.io/zh/) 进行导入, 例如 ```lua on_run(function (target) -- 导入工程类 import("core.project.project") -- 获取当前工程目录 print(project.directory()) end) ``` 详细的扩展类库使用,见 [import](https://xmake.io/zh/) 一些内建类库有: * os: 系统类库 * string: 字符串类库 * path: 路径类库 * table: table和array处理 * io: 文件io处理 * coroutine: 协程类库 一些内建的api有: * raise:引发异常 * try/catch/finally: 异常捕获处理 * print/printf:打印 * format: 格式化字符串 更多详细类库和内建api介绍,见后续介绍。。。