Luamake Build System
基于 Lua 的构建系统,生成 Ninja 构建文件。
快速开始
local lm = require "luamake"
lm:exe "myapp" {
sources = { "src/*.c", "!src/test.c" },
includes = "include",
defines = { "DEBUG", "VERSION=1.0" },
}
luamake # 构建
luamake clean # 清理
luamake rebuild # 重建
目标类型
| API | 说明 | 输出 | |-----|------|------| | lm:exe | 可执行文件 | app.exe / app | | lm:dll | 动态库 | lib.dll / lib.so / lib.dylib | | lm:lib | 静态库 | lib.lib / lib.a | | lm:source_set | 源码集(不链接,用于复用,通过 deps 引用) | 无 | | lm:lua_src | Lua C 模块(静态嵌入到 lm:lua_exe) | 无 | | lm:lua_dll | Lua C 模块(动态加载) | module.dll / module.so | | lm:lua_exe | 内嵌 Lua 的可执行文件 | app.exe / app | | lm:lua_embed | Lua 嵌入资源(按命名组嵌入 Lua 文件,生成 source_set;自动导出 includes/objdeps) | 无(source_set) | | lm:phony | 伪目标(聚合依赖) | 无 |
目标属性
源文件
| 属性 | 说明 | 示例 | |------|------|------| | sources | 源文件 | "src/*.cpp", { "a.cpp", "b.cpp" } | | includes | 包含目录 | "include", { "include", "3rd" } | | sysincludes | 系统包含目录(抑制警告) | "/usr/local/include" | | defines | 预处理器定义 | { "DEBUG", "VER=1" } | | objdeps | 对象依赖(编译前必须生成) | "generated_header" |
objdeps用法:当源文件依赖代码生成产物(如生成的头文件)时,需用objdeps声明依赖的目标名,确保生成步骤在编译前完成。deps只控制链接顺序,不能保证编译前生成文件。
```lua
lm:rule "gen_header" { ... } -- 生成头文件的规则
lm:exe "app" {
sources = "src/*.cpp",
objdeps = "gen_header", -- 编译 src/*.cpp 前先执行 gen_header
}
```
注意:lm:lua_embed目标会自动导出includes和objdeps,通过deps依赖它的目标无需手动声明这两项。详见下方lm:lua_embed用法。
链接
| 属性 | 说明 | 示例 | |------|------|------| | links | 链接库 | { "pthread", "ws2_32" } | | linkdirs | 库搜索目录 | "lib", { "lib", "/usr/lib" } | | ldflags | 链接器标志 | "-Wl,--as-needed" | | frameworks | macOS/iOS 框架 | { "Foundation", "Cocoa" } | | deps | 目标依赖 | "lib1", { "lib1", "lib2" } |
编译
| 属性 | 说明 | 示例 | |------|------|------| | flags | 编译器标志 | { "-Wall", "-O2" } | | cflags | C 编译器标志 | "-std=c11" | | cxxflags | C++ 编译器标志 | "-std=c++20" | | confs | 引用命名配置 | "myconfig" |
注意:cflags/cxxflags直接传递给编译器命令行。在lm:conf中可用c = "c11"/cxx = "c++20"作为简写,luamake 会自动转换为对应编译器的标准标志(如 MSVC 的/std:c11)。
特殊目标用法
lm:source_set — 将源文件组织为可复用的源码集,通过 deps 引用,不单独链接:
-- 定义共享源码集
lm:source_set "common" {
includes = "include",
sources = { "src/common/*.cpp" },
}
-- 多个目标复用同一组源文件
lm:exe "app1" { deps = "common", sources = "src/app1.cpp" }
lm:exe "app2" { deps = "common", sources = "src/app2.cpp" }
lm:lua_src — 将 Lua C 模块静态嵌入到 lm:lua_exe 中:
lm:lua_src "mymodule" {
sources = "src/mymodule.c",
}
lm:lua_exe "app" {
deps = "mymodule",
sources = "src/main.cpp",
}
lm:lua_embed — 将 Lua 文件按命名组嵌入到 C 源码中,生成一个 source_set 供其他目标依赖。data 下每个 key 是一个组,组名直接成为生成的 lua_embed_bundle 结构体的字段名。每组可独立设置 bytecode = true。bee_glue = true 启用 bee 胶水层,约定 data.preload 注入 _PRELOAD、data.main[1] 作为入口。自动向依赖方导出 includes 和 objdeps,通过 deps 依赖时无需手动声明。完整规则(分组语义、pattern 语法、字节码取舍、bee_glue 契约、无胶水时的接入姿势)见 references/advanced/lua_embed.md:
lm:lua_embed "myembed" {
bee_glue = true,
data = {
main = { bytecode = true, "src/main.lua" },
preload = { bytecode = true, { dir = "lualib" } },
data = { { name = "config.json", file = "assets/config.json" } },
},
}
lm:lua_src "glue" {
deps = "myembed", -- 自动获取 includes 和 objdeps
includes = "3rd/bee.lua",
sources = "src/glue.cpp",
}
lm:lua_exe "app" { deps = { "glue", "myembed" }, sources = "src/main.cpp", }
**`lm:phony`** — 聚合多个目标为一个命名依赖,方便统一引用:
lm:phony "all_libs" { deps = { "libfoo", "libbar", "libqux" }, }
lm:exe "app" { deps = "all_libs", sources = "main.cpp", }
---
## 配置 (lm:conf)
`lm:conf` 有两种用法,行为完全不同,不要混淆:
### 匿名配置:`lm:conf { ... }` — 立即生效,影响全局
不带名称调用时,属性**立即应用到当前工作区的所有后续目标**,相当于设置全局默认值。
-- 所有参数均为可选,按项目实际需要填写 lm:conf { c = "c11", -- C 标准:"c89", "c99", "c11", "c17" cxx = "c++20", -- C++ 标准:"c++11", "c++14", "c++17", "c++20" visibility = "hidden", -- 符号可见性:"hidden"(默认隐藏)或 "default"(默认可见) }
-- 以下目标自动继承上面的配置 lm:exe "app1" { sources = "src/app1.cpp" } lm:exe "app2" { sources = "src/app2.cpp" }
**适用场景**:项目中大部分目标共享相同的编译标准、模式等基础配置时使用。
### 命名配置:`lm:conf "name" { ... }` — 按需引用,不自动生效
带名称调用时,属性**不会立即生效**,仅存储起来,需要目标通过 `confs` 属性显式引用。
-- 定义命名配置(不会自动应用到任何目标) lm:conf "mylib" { defines = "MYLIB_API", includes = "3rd/mylib", }
-- 通过 confs 引用,只有这个目标会使用该配置 lm:exe "app" { confs = "mylib", sources = "main.cpp", }
**适用场景**:多个目标需要共享同一组特定配置(如第三方库的头文件路径和宏定义),但不希望污染全局时使用。
### 如何选择
| 场景 | 用法 |
|------|------|
| 设置项目范围的 C/C++ 标准、构建模式等 | `lm:conf { ... }` |
| 为特定的几个目标共享一组 includes/defines | `lm:conf "name" { ... }` + `confs = "name"` |
| 只有一个目标需要特定配置 | 直接写在目标属性里,无需 conf |
---
## 内置变量
### 路径变量
| 变量 | 说明 | 示例 |
|------|------|------|
| `$builddir` | 构建目录(默认 `build`,可自定义) | `build` |
| `$bindir` | 二进制输出目录 | `build/bin` |
| `$objdir` | 对象文件目录 | `build/obj` |
| `$bin` | `$bindir` 别名 | |
| `$obj` | `$objdir` 别名 | |
| `$in` | 输入文件 (规则中) | |
| `$out` | 输出文件 (规则中) | |
> **自定义构建目录**:当项目已使用 `build` 目录时,可在脚本开头修改构建目录:
> ```lua
> lm.builddir = "_build" -- 构建产物将输出到 _build/ 目录
> ```
> `$bindir` 和 `$objdir` 默认基于 `$builddir`,修改后会自动跟随变化。
### 全局变量
| 变量 | 说明 | 示例值 |
|------|------|--------|
| `lm.os` | 目标 OS | `"windows"`, `"linux"`, `"macos"`, `"ios"`, `"android"` |
| `lm.compiler` | 编译器 | `"msvc"`, `"gcc"`, `"clang"` |
| `lm.arch` | 架构 | `"x86_64"`, `"x86"`, `"arm64"` |
| `lm.mode` | 构建模式 | `"debug"`, `"release"` |
| `lm.workdir` | 工作目录 | 项目根目录 |
---
## 开发工作流
编写或修改 `make.lua` 构建脚本后,**必须**运行构建命令验证脚本的正确性:
1. 运行构建,验证脚本是否有语法错误或配置问题
luamake
2. 如果构建失败,根据错误信息修复脚本后重新运行
luamake
3. 如果需要从干净状态重建(排除增量构建的干扰)
luamake rebuild
### 注意事项
- **Windows 平台**:必须使用 **PowerShell** 运行 `luamake` 命令,不要使用 Git Bash。
- 每次修改 `make.lua` 后都要运行 `luamake` 确认构建成功,不要只写脚本不验证。
- 关注构建输出中的**警告和错误信息**,它们通常指向配置问题(如路径错误、缺少依赖等)。
- 如果构建失败,优先分析错误信息,修复后再次运行验证,直到构建通过。
---
# 最佳实践
| 主题 | 文档 | 包含内容 |
|------|------|----------|
| 依赖管理 | `references/best_practices/bp_dependency.md` | deps 定义顺序、源码集复用、增量源码集、动态模块发现 |
| 条件编译与静态分析 | `references/best_practices/bp_compilation.md` | 条件编译、静态分析集成 |
| 代码生成与 objdeps | `references/best_practices/bp_codegen.md` | 代码生成管道、objdeps 用法 |
| Lua 模块与测试 | `references/best_practices/bp_lua_and_test.md` | Lua C 模块、内置测试框架 |
### 其他详细文档
| 主题 | 文档 |
|------|------|
| Bee 运行时库(API 参考来源、模块列表、Meta 文件用法) | `references/advanced/bee_runtime.md` |
| `lm:lua_embed` 权威参考(分组 / pattern / bytecode / bee_glue / 自定义接入) | `references/advanced/lua_embed.md` |
---
## Bee 运行时库
`luamake lua script.lua` 运行脚本时预加载 bee 库。API 参考来源及模块列表详见 `references/advanced/bee_runtime.md`。
### 适用边界
优先在以下场景使用本 skill:
- 当前仓库明确使用 `luamake` 或通过 `make.lua` 定义构建目标。
- 用户正在修改 `make.lua`、排查 `luamake` 报错,或理解 `deps`、`objdeps`、`lm:conf`、目标类型、构建目录等机制。
- 用户讨论的问题与当前项目的 Ninja 生成流程、Bee 集成或 Lua C 模块构建直接相关。
以下场景通常不应使用本 skill:
- 与当前项目无关的通用 C/C++ 编译、链接、优化理论问题。
- 纯 `CMake`、`xmake`、`Bazel`、`Meson` 等其他构建系统的问题。
- 只是在问编译器语法、语言标准或平台 API,而没有落到 `luamake` / `make.lua` 的构建脚本上。
---
## 故障排除
| 问题 | 解决方案 |
|------|----------|
| "no source files found" | 检查 glob 模式,确认 `rootdir`,检查文件扩展名 |
| Windows 链接错误 | 添加系统库:`links = { "ws2_32", "iphlpapi", "user32" }` |
| 交叉编译问题 | 使用平台特定配置,检查 `lm.compiler` |
| 缺少生成文件 | 添加 `objdeps` 确保生成后再编译 |
| `build` 目录冲突 | 项目已使用 `build` 目录时,用 `lm.builddir = "_build"` 修改构建目录 |
| 找不到头文件 | 检查 `includes` 路径,使用绝对路径或 `$builddir` |
| Ninja 构建失败 | 删除 `build/` 目录重新构建 |