# makebr **Repository Path**: graviton/makebr ## Basic Information - **Project Name**: makebr - **Description**: Make files battle royale Make 系统大逃杀. - **Primary Language**: Unknown - **License**: AGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-18 - **Last Updated**: 2026-09-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # makebr — 同一份 Go 项目的构建系统实验 用相同的源码、编译参数、产物命名和部署流程,比较各种 make / build / task 系统。 现在可以直接在本仓库运行,不需要访问内网或另一个业务仓库。 第一批:**Go 脚本、GNU Make、just、Task、Mage、Ninja、CMake**。 第二批:**Invoke、doit、Deno、xmake**。 第三批:**SCons、Meson、Rake、Babashka**。 第四批:**Bun scripts、Gulp(Bun 运行)、mise、cargo-make、Ant、Gradle**,目前共二十一种。 示例程序只依赖 Go 标准库;Mage、Invoke、doit 等属于构建工具依赖。先覆盖 Windows / macOS / Linux 上通用的方案,再加入 Bazel、容器构建和更多语言生态选手。 ## 快速开始 新机器可以先使用 [环境准备脚本](docs/bootstrap.md): ```text python scripts/bootstrap.py --plan python scripts/bootstrap.py --install ``` 成功后 PowerShell 执行 `. ./.tools/env.ps1`,Linux/macOS 执行 `. ./.tools/env.sh`, 即可在当前终端调用本地安装的工具。脚本启动需要 Python 3.8+,会检查/补齐 Go、uv、 构建工具及 Bun/Ruby/JDK 等所选方案需要的运行环境。 安装 Go(示例代码要求 1.22+),在仓库根目录执行: ```text go run ./tools/build go run ./tools/build check go run ./tools/build all go run ./tools/build deploy ``` 最后一条把**本机平台**的程序安装到 `dist/go/`,生成 SHA-256 清单。 Windows 上执行 `./dist/go/hello.exe --json --name makebr`;Linux amd64 上执行 `./dist/go/hello --json --name makebr`;macOS arm64 上执行 `./dist/go/hello-darwin-arm64 --json --name makebr`。 这里的部署是本地目录安装,便于重复实验;没有启动后台服务、修改系统 PATH、 上传服务器或覆盖其他项目的程序。 工具选型见 [评价与推荐](docs/recommendations.md):通用跨平台入口优先 Task, 简单命令入口选 just,当前 Linux 环境可优先 Make,只依赖 Go 时选纯 Go 驱动。 旧版 CentOS Docker 可按 [无 Ruby/JDK 的准备方法](docs/bootstrap.md#centos-light) 运行其中十八种方案;[Linux 实测](docs/results-linux-docker.md) 已通过 528 个命令步骤。 ## 统一赛题 每种方案都支持:本机构建、指定平台交叉编译、六平台发布、`fmt / vet / test / check`、 本机部署和清理。编译命令统一为: ```text CGO_ENABLED=0 GOOS= GOARCH= go build -trimpath "-ldflags=-s -w" -o <产物> ./cmd/hello ``` 上面是参数说明;Windows 环境变量由各工具设置,不直接粘贴这行到 PowerShell。 发布目标仅限 `linux / darwin / windows` × `amd64 / arm64`。 每个方案独占 `build/<工具>/` 和 `dist/<工具>/`,避免互相覆盖或借用增量结果。 | 目标 | `build/<工具>/` 下的文件名 | | --- | --- | | linux/amd64 | `hello` | | linux/arm64 | `hello-linux-arm64` | | darwin/amd64 | `hello-darwin-amd64` | | darwin/arm64 | `hello-darwin-arm64` | | windows/amd64 | `hello.exe` | | windows/arm64 | `hello-windows-arm64.exe` | 这个命名故意保留了旧实验里的条件分支:无平台后缀的 `hello` 始终是 Linux amd64。 `deploy` 总是依赖本机产物,即便刚刚构建的是 Linux 交叉编译目标。 非本机的发布产物只检查元数据,不尝试执行。 ## 二十一种方案怎么运行 以下命令都在仓库根执行。额外工具的安装见 [安装说明](docs/tools.md)。 | 方案 | 本机构建 | 指定平台 | 六平台发布 | 本机部署 | | --- | --- | --- | --- | --- | | Go | `go run ./tools/build` | `go run ./tools/build build linux/arm64` | `go run ./tools/build all` | `go run ./tools/build deploy` | | Make | `make` | `make TARGET=linux/arm64` | `make -j6 all` | `make deploy` | | just | `just` | `just build linux/arm64` | `just all` | `just deploy` | | Task | `task` | `task build TARGET=linux/arm64` | `task all` | `task deploy` | | Mage | `mage` | `mage build linux/arm64` | `mage all` | `mage deploy` | | Ninja | `ninja -f build/ninja-config/build.ninja` | `ninja -f build/ninja-config/build.ninja build-linux-arm64` | `ninja -f build/ninja-config/build.ninja all` | `ninja -f build/ninja-config/build.ninja deploy` | | CMake | `cmake --build build/cmake-config` | `cmake --build build/cmake-config --target build-linux-arm64` | `cmake --build build/cmake-config --target release` | `cmake --build build/cmake-config --target deploy` | | Invoke | `uv run invoke` | `uv run invoke build --target linux/arm64` | `uv run invoke all` | `uv run invoke deploy` | | doit | `uv run doit` | `uv run doit build:linux-arm64` | `uv run doit all` | `uv run doit deploy` | | Deno | `deno task build` | `deno task build linux/arm64` | `deno task all` | `deno task deploy` | | xmake | `xmake` | `xmake build build-linux-arm64` | `xmake build release` | `xmake build deploy` | | SCons | `uv run scons` | `uv run scons TARGET=linux/arm64` | `uv run scons all` | `uv run scons deploy` | | Meson | `uv run meson compile -C build/meson` | `uv run meson compile -C build/meson build-linux-arm64` | `uv run meson compile -C build/meson release` | `uv run meson compile -C build/meson deploy` | | Rake | `bundle exec rake` | `bundle exec rake build TARGET=linux/arm64` | `bundle exec rake all` | `bundle exec rake deploy` | | Babashka | `bb build` | `bb build linux/arm64` | `bb all` | `bb deploy` | | Bun scripts | `bun run build` | `bun run build linux/arm64` | `bun run all` | `bun run deploy` | | Gulp | `bun node_modules/gulp/bin/gulp.js build` | `bun node_modules/gulp/bin/gulp.js build --target linux/arm64` | `bun node_modules/gulp/bin/gulp.js all` | `bun node_modules/gulp/bin/gulp.js deploy` | | mise | `mise run build` | `mise run build-linux-arm64` | `mise run all` | `mise run deploy` | | cargo-make | `makers build` | `makers build-linux-arm64` | `makers all` | `makers deploy` | | Ant | `ant` | `ant build-linux-arm64` | `ant all` | `ant deploy` | | Gradle | `gradle build` | `gradle build-linux-arm64` | `gradle release` | `gradle deploy` | Ninja、CMake 和 Meson 需要先生成构建图: ```text go run ./tools/ninjagen cmake -S . -B build/cmake-config -G Ninja uv run meson setup build/meson --backend=ninja ``` CMake 也能在配置时设置默认平台,例如 `cmake -S . -B build/cmake-config -G Ninja -DTARGET=linux/arm64`。 恢复本机默认值需要重新传入本机平台,或清除 CMake 的配置缓存。 各工具使用同名的 `fmt`、`vet`、`test`、`check`、`clean` 目标;Go 使用 `go run ./tools/build <目标>`,Ninja 使用 `ninja -f build/ninja-config/build.ninja <目标>`。 CMake 使用 `cmake --build build/cmake-config --target <目标>`,但完整清理目标叫 `purge`(`clean` 是生成器保留目标),它保留 `build/cmake-config/` 配置目录。 Ninja 的图和日志同样保留在 `build/ninja-config/`。`check` 会先格式化源码,再运行静态检查和测试。 Invoke/doit 的依赖通过 `uv sync --locked` 安装。doit 的完整清理命令是 `uv run doit purge`,其 `.doit.db` 数据库保留;`uv run doit -n 6 all` 可并行构建。 xmake 的检查和清理使用 **`xmake build check` / `xmake build purge`**, 其他门禁同样使用 `xmake build fmt|vet|test`;不要与 xmake 自己的内置命令混淆。 xmake 的 `.xmake/` 配置和 `build/xmake-cache/` 依赖缓存保留,缺失产物会强制重建。 Deno 的 `deno task check` 还会先对 TypeScript 驱动做类型检查。 SCons 使用 `uv run scons purge` 完整清理;`uv run scons -j6 all` 可以并行构建。 Meson 的 Go 门禁名为 `go-fmt / go-vet / go-test / go-check`,例如 `uv run meson compile -C build/meson go-check`。完整清理使用同一路径下的 `purge` 目标。 Meson 要求产物在构建目录内,所以 `build/meson/` 同时存放图、日志和程序; `purge` 只删除六个程序和 `dist/meson/`,保留配置以便随后重建。 Rake 首次使用先执行 `bundle install`,版本由 `Gemfile.lock` 固定。 Babashka 使用独立的 `bb` 二进制,不需要 JVM 或完整 Clojure 工具链。 Bun/Gulp 首次先执行 `bun install --frozen-lockfile`,不使用 npm。Gulp 直接通过 Bun 运行包内 CLI;本例不需要 Node.js。mise 首次运行需 `mise trust mise.toml`,或者使用 bootstrap 生成的环境脚本,仅信任本仓库路径。cargo-make 使用独立 `makers`, 本例没有 Cargo 项目和 Rust 编译步骤。Ant/Gradle 需要 JDK(本机使用 21)。 Gradle 使用 `release` 避免与全局选项歧义;本版关闭常驻 daemon,计时包含 JVM 启动成本。 ## 目前能比较什么 | 方案 | 任务/依赖如何表达 | 本实现的增量行为 | 跨平台代价 | | --- | --- | --- | --- | | Go | Go 分派和循环 | 每次调用 `go build`,由 Go 缓存编译 | 只需要 Go | | Make | 文件目标、依赖、mtime | 未变化的目标跳过整个命令 | 需要 GNU Make;本例没有 Unix shell 专用命令 | | just | recipe、参数和任务依赖 | 每次执行 recipe | Windows 用自带 PowerShell,Unix 用 sh;环境赋值需分支 | | Task | YAML、模板、任务图 | 文件内容指纹;缓存按平台隔离 | 内嵌 shell,不需要 Bash | | Mage | Go 函数、`mg.Deps` | 每次调用 Go;任务内依赖去重,六平台并发 | Mage 和一个 Go 模块依赖 | | Ninja | 显式文件构建图 | mtime + 命令变更 | 自己不提供循环/源码 glob,需要生成器 | | CMake | 元构建系统生成 Ninja 图 | 由生成的 Ninja 图判断 | 配置阶段 + CMake + Ninja;不需要 C/C++ 编译器 | | Invoke | Python 函数、装饰器、前置任务 | 每次运行 Go;发布顺序执行 | Python + uv 锁定的 Invoke;用 argv 子进程避开 shell 引号 | | doit | Python 生成文件依赖图 | 文件签名 + 每平台 targets + 输入列表变化检查 | Python + uv 锁定的 doit;本例使用 SQLite 保存状态 | | Deno | 内置 task + TypeScript + Promise | 每次运行 Go;六平台并发 | 一个 Deno 二进制,无 npm/JSR 依赖 | | xmake | Lua 目标图 + `depend.on_changed` | mtime、文件列表和参数;每平台独立缓存 | xmake + Go;无 C/C++ 编译步骤 | | SCons | Python 文件节点、Action、Value | 文件内容签名、命令参数和输入列表 | uv 中的 SCons;显式禁用默认 C/C++ 工具探测 | | Meson | 声明式描述生成 Ninja 图 | 输入签名变化 → Ninja restat 决定重建 | uv 中的 Meson + Ninja;小型 Python 输入扫描器 | | Rake | Ruby file/task 和前置依赖 | 文件与源码目录的 mtime | Ruby + Bundler 锁定的 Rake | | Babashka | EDN 任务 + Clojure 函数 | 每次调用 Go;发布顺序执行 | 独立 bb 二进制,无外部 Clojure 依赖 | | Bun scripts | package.json + JavaScript | 每次调用 Go;发布顺序执行 | Bun,无额外构建包 | | Gulp | JavaScript `series / parallel` | 每次调用 Go;发布并发 | Bun + bun.lock 中的 Gulp 5;用回调完成任务 | | mise | TOML 任务与环境模板 | sources/outputs mtime;六个平台任务 | mise 二进制;运行可信配置 | | cargo-make | TOML 任务依赖和条件分派 | 每次调用 Go;发布顺序执行 | 独立 makers;复用内置命令/环境能力 | | Ant | XML target、macrodef、exec | 本例每次执行 Go | JDK + Ant,无第三方 Ant task | | Gradle | Groovy Exec 任务及 inputs/outputs | 原生输入指纹,跳过 UP-TO-DATE 任务 | JDK + Gradle,无 Maven 依赖;JVM 启动较重 | 这里区分**工具跳过任务**和 **Go 编译缓存命中**。Mage 本版没有另写一套 mtime 缓存;它也不要求独立模块或修改 `go.work`。这些都是项目布局和实现选择,不能当成 工具的必然限制。Go 和 Mage 共享 Go 编译原语;其他方案各自调用 `go build`、 表达命名规则并维护自己的构建图。所有方案共享文件复制、清理和清单生成,见 `internal/lab` 与 `tools/ops`,这部分开销也计入部署时间。 Invoke、doit 和 SCons 还共享 `scripts/python_build.py` 中的命名、参数和进程环境原语; 它们各自定义任务图,doit 用 `CmdAction(shell=False)` 直接执行 Go。 Meson 的编译命令在 `meson.build` 中定义,输入扫描器仅生成内容签名; `go-check` 复用纯 Go 驱动中顺序执行的格式化、静态检查与测试门禁。 Bun/Gulp 共享 `scripts/js_build.mjs` 的参数、命名和进程调用;Gulp 自己负责串并行任务图。 mise/Gradle 的 check 门禁也复用纯 Go 驱动,编译仍由各自任务直接调用 Go。 Make/Ninja/CMake 的输入依赖针对这个固定小项目,包含源码目录以识别文件增删。 Task 指纹包含自身配置,并用 `label: build-<平台>` 隔离缓存。当前实验没有追踪 Go 工具链升级、`GOFLAGS`、`GOAMD64` 等环境变化;更换这些条件后请清理重建。 也不把 Go module 缓存命中等同于密闭构建或可复现构建。 ## 验证与实测 ```text uv sync --locked uv run main.py uv run main.py --tools go task ninja uv run main.py --tools invoke doit deno xmake --mutations uv run main.py --tools scons meson rake babashka --mutations uv run main.py --tools bun gulp mise cargo-make ant gradle --mutations uv run main.py --mutations ``` 终端输出按工具和验证阶段分组:绿色 `OK`、黄色 `EXPECTED FAIL`(预期失败探针)、 红色 `FAIL`,最后汇总结果、步骤数和耗时。颜色默认自动检测,重定向输出时关闭; 可用 `--color always` / `--color never` 强制控制,也支持 `NO_COLOR` 环境变量。 编排使用仓库的 uv 项目和 `.venv`(Python 3.14,依赖由 `uv.lock` 锁定)。 也可直接运行 `python scripts/verify.py`;验证器只使用标准库,兼容 Python 3.8+。 它逐个检查清理、六平台产物的 Go 元数据、本机程序输出、 部署清单和字节校验和、重复构建和重复部署。`--mutations` 还临时改动源码, 检查两个不同平台的缓存均被正确更新、源码文件增删能触发重建, 并注入一个失败测试检查退出码传播。 源码通过 `finally` 恢复;运行期间不要同时编辑源码或运行其他构建。 `Ctrl+C` 的正常 Python 清理会恢复文件,强制终止进程仍可能中断恢复。 完整命令、版本、耗时和输出写入 `build/verification/report.json`。 缺工具会明确报错,不会把跳过当成通过。可以用 `MAKEBR_MAKE`、`MAKEBR_JUST` 等环境变量指定工具可执行文件。 Rake/Babashka 对应的可执行文件覆盖变量为 `MAKEBR_RUBY` / `MAKEBR_BB`。 验证器通过 `ruby -S bundle exec rake` 调用 Rake,避开 Windows `.bat` 启动器。 [本机实测记录](docs/results-windows.md) 单独记录平台和测试范围。 [第二批方案与扩展验证](docs/results-wave2.md) 记录新增工具的实测与依赖。 [第三批方案与实测](docs/results-wave3.md) 记录 SCons、Meson、Rake 和 Babashka。 [第四批方案与实测](docs/results-wave4.md) 记录 Bun、Gulp、mise、cargo-make、Ant 和 Gradle。 [CentOS 8 Docker 实测](docs/results-linux-docker.md) 记录十八种方案的 Linux 宿主验证、 版本、耗时及 glibc 兼容处理;[评价与推荐](docs/recommendations.md) 按测试数据、 编写成本、功能和使用体验给出选择建议。 GitHub Actions 配置了 Windows / Linux / macOS 三平台验证;本地交叉编译成功 不代表已经在另外两个系统上执行过全部工具。耗时只是流程观测,不能据此给工具排性能榜。 ## 文件树 ```text cmd/hello/ # 所有方案共同构建的 CLI internal/greeting/ # 示例业务逻辑及测试 internal/lab/ # Go/Mage 编译原语、跨平台部署和清理 tools/build/ # 纯 Go 基线 tools/ops/ # 其他方案使用的部署/清理叶子操作 tools/ninjagen/ # Ninja 构建图生成器 Makefile # GNU Make justfile # just Taskfile.yml # Task magefile.go # Mage CMakeLists.txt # CMake → Ninja tasks.py # Invoke dodo.py # doit deno.json / tools/deno.ts # Deno task + TypeScript xmake.lua # xmake SConstruct # SCons meson.build # Meson → Ninja Rakefile / Gemfile.lock # Ruby Rake bb.edn / scripts/makebr/ # Babashka package.json / bun.lock # Bun scripts 与 Gulp 依赖 gulpfile.mjs # Gulp(由 Bun 运行) mise.toml # mise Makefile.toml # cargo-make build.xml # Ant build.gradle # Gradle scripts/bootstrap.py # 检查、安装和激活实验环境 scripts/meson_inputs.py # Meson 输入签名扫描器 scripts/python_build.py # Python 构建方案共享的命令原语 scripts/verify.py # 统一契约验证 main.py / pyproject.toml # uv 编排入口与 Python 环境 docs/ # 工具安装、实测、调研笔记 archive/synccase/ # 原 specimens 标本,依赖原业务仓库 build/<工具>/ # 构建产物(忽略提交) dist/<工具>/ # 本地部署与 manifest.json(忽略提交) ``` [调研笔记](docs/in.md) 保留候选工具清单; [旧 synccase 实测](docs/synccase-notes.md) 保留原场景与历史结论。 更多候选及它们值得比较的机制见 [参赛路线图](docs/contenders.md)。