diff --git a/docs/api/description/builtin-rules.md b/docs/api/description/builtin-rules.md index 97f42c35..808e9dcb 100644 --- a/docs/api/description/builtin-rules.md +++ b/docs/api/description/builtin-rules.md @@ -700,6 +700,10 @@ cat build/.gens/test/macosx/x86_64/release/rules/c++/bin2c/image.png.h If you are using a compiler that supports the C23 `#embed` feature (such as clang or gcc), you can also use the `#embed` directive directly to embed binary files. You need to set the C23 language standard first via `set_languages("c23")`, and then use [add_embeddirs](project-target.md#add_embeddirs) to set the search path. This approach is more aligned with the C23 standard and does not require generating additional header files. ::: +### Transform + +Since v3.1.0, binary files can be transformed before being generated as a header. Transform functions are assigned in the same way as [utils.bin2obj](#transform-1). + ## utils.bin2obj New rule added in v3.0.6 to convert binary files to object files and link them into the target program. @@ -721,7 +725,7 @@ target("myapp") add_files("assets/data.bin", {zeroend = true}) ``` -### Access Data +### Accessing Data In C/C++ code, we can access the embedded data via symbols. The symbol name generation rule is: `_binary__start` and `_binary__end`. Non-alphanumeric characters in the filename are replaced with underscores. @@ -746,6 +750,68 @@ int main() { } ``` +### Transform + +Since v3.1.0, binary files can be transformed before converting into object files by using the newly added `transform` parameter. For example, an asset file can be compressed using LZ4 ([core.compress.lz4](../scripts/extension-modules/core/compress/lz4.md)) before embedding into the application. See below for detailed usage. + +#### Inline Function + +Inline function can be used to transform the binary file. Suitable for short and simple conversions. + +```lua +target("test") + set_kind("binary") + add_rules("utils.bin2obj") + add_files("src/*.c") + add_files("src/asset.bin", {transform = function (inputfile, outputfile, opt) + import("core.base.bytes") + local data = io.readfile(inputfile, {encoding = "binary"}) + io.writefile(outputfile, data:reverse(), {encoding = "binary"}) + end}) +``` + +::: tip WARNING +Inline transform functions are not compatible with [generated projects](../../guide/extensions/builtin-plugins.md#generate-ide-project-files). Write the function in a lua file instead. +::: + +#### Function In Lua File + +For more complex transforms, writing the function in a separate lua file keeps the code clear. + +`xmake.lua`: +```lua +target("test") + set_kind("binary") + add_rules("utils.bin2obj") + add_files("src/*.c") + add_files("src/asset.bin", {transform = path.join(os.projectdir(), "transform.lua")}) +``` + +`transform.lua`: +```lua +function main(inputfile, outputfile) + import("core.base.bytes") + local data = io.readfile(inputfile, {encoding = "binary"}) + io.writefile(outputfile, data:reverse(), {encoding = "binary"}) +end +``` + +#### Per-rule Transform Function + +Besides writing a function for each file or file group, a transform function can also be applied across the entire rule scope. + +```lua +add_rules("utils.bin2obj", { + transform = path.join(os.projectdir(), "transform.lua") +}) +``` + +::: tip NOTE +- Remember to pass argument `{encoding = "binary"}` to `io.readfile` or `io.writefile` when reading or writing binary data +- It's preferred to pass an absolute path (formed by joining `os.projectdir()` with the relative path) to the transform lua file +- See [core.base.bytes](../scripts/extension-modules/core/base/bytes.md) for handling binary data +::: + ## utils.replace This rule applies in-memory text substitutions on a source file before it is fed to the compiler. The rewritten file is written under the target's auto-generated directory, and the original file's directory is automatically added to `includedirs` so relative `#include` directives in the rewritten file still resolve. diff --git a/docs/zh/api/description/builtin-rules.md b/docs/zh/api/description/builtin-rules.md index 13a93a4e..167fac8b 100644 --- a/docs/zh/api/description/builtin-rules.md +++ b/docs/zh/api/description/builtin-rules.md @@ -689,6 +689,10 @@ cat build/.gens/test/macosx/x86_64/release/rules/c++/bin2c/image.png.h 如果你使用支持 C23 `#embed` 特性的编译器(如 clang 或 gcc),也可以直接使用 `#embed` 指令来嵌入二进制文件。需要先通过 `set_languages("c23")` 设置 C23 语言标准,然后使用 [add_embeddirs](project-target.md#add_embeddirs) 来设置搜索路径。这种方式更符合 C23 标准,无需生成额外的头文件。 ::: +### 转换函数 + +在v3.1.0中,xmake添加了转换函数接口。利用该接口,可以自定义被生成为头文件的二进制数据。用法请参考 [utils.bin2obj](#转换函数-1) + ## utils.bin2obj v3.0.6 以上版本可以使用此规则,相比 `utils.bin2c` 具有极快的构建速度。因为它跳过了 C 代码生成和编译步骤,直接生成对象文件(COFF, ELF, Mach-O)参与链接。 @@ -736,6 +740,68 @@ int main() { 此外,`glsl2spv` 和 `hlsl2spv` 规则也新增了对 `bin2obj` 的支持,可以直接将编译后的 SPIR-V 文件作为对象文件嵌入。 +### 转换函数 + +在v3.1.0中,xmake添加了转换函数接口。利用该接口,可以自定义被生成为对象文件的二进制数据,如使用LZ4([core.compress.lz4](../scripts/extension-modules/core/compress/lz4.md))压缩文件后再生成为对象文件,嵌入程序中。 + +#### 内联转换函数 + +转换函数可内联在描述域中,适合简单的转换: + +```lua +target("test") + set_kind("binary") + add_rules("utils.bin2obj") + add_files("src/*.c") + add_files("src/asset.bin", {transform = function (inputfile, outputfile, opt) + import("core.base.bytes") + local data = io.readfile(inputfile, {encoding = "binary"}) + io.writefile(outputfile, data:reverse(), {encoding = "binary"}) + end}) +``` + +::: tip 警告 +内联转换函数无法用于生成的第三方工程文件(见[生成IDE工程文件](../../guide/extensions/builtin-plugins.md#generate-ide-project-files))。如有需要,请使用Lua文件形式 +::: + +#### Lua文件形式 + +对于更加复杂的转化,可以将转换函数单独写在Lua文件中: + +`xmake.lua`: +```lua +target("test") + set_kind("binary") + add_rules("utils.bin2obj") + add_files("src/*.c") + add_files("src/asset.bin", {transform = path.join(os.projectdir(), "transform.lua")}) +``` + +`transform.lua`: +```lua +function main(inputfile, outputfile) + import("core.base.bytes") + local data = io.readfile(inputfile, {encoding = "binary"}) + io.writefile(outputfile, data:reverse(), {encoding = "binary"}) +end +``` + +#### 规则级转换函数 + +转换函数不仅可作用于文件或者文件组,还可以写成以下的形式,以作用于整个规则: + +```lua +add_rules("utils.bin2obj", { + transform = path.join(os.projectdir(), "transform.lua") +}) +``` + +::: tip 提示 +- 在使用`io.readfile`或`io.writefile`处理二进制时,记得加入`{encoding = "binary"}`参数 +- 使用Lua形式转换函数时,最好传入由`path.join(os.projectdir(), <路径>)`得到的绝对路径 +- 可参考xmake提供的二进制处理API:[core.base.bytes](../scripts/extension-modules/core/base/bytes.md) +::: + ## utils.replace 此规则会在源码送入编译器之前对其做文本替换。替换后的文件会写到目标的自动生成目录下,同时原始文件所在目录会被自动加入 `includedirs`,所以替换后文件里的相对 `#include` 依然能正确解析。