Erlang依赖管理全攻略:erlang.mk的deps、git依赖、hex依赖与缓存机制实战指南
【免费下载链接】erlang.mkA build tool for Erlang that just works.项目地址: https://gitcode.com/gh_mirrors/er/erlang.mk
erlang.mk是一款"开箱即用"的 Erlang 构建工具,也是 Erlang 生态中最流行的构建器之一。本文带你一次性掌握 erlang.mk 的依赖管理核心玩法:如何用DEPS声明依赖、如何拉取git 依赖与hex 依赖、如何用CACHE_DEPS开启依赖缓存加速构建。无论是新手还是想给项目提速的老手,读完这篇完整指南都能直接上手。
一、为什么选择 erlang.mk 管理依赖?
Erlang.mk 的口号是 "A build tool for Erlang that just works"(一个真正好用的 Erlang 构建工具)。在依赖管理方面,它的核心优势有:
- 🎯一行代码声明依赖:只需
DEPS = cowboy,make会自动拉取、编译、并按正确顺序构建所有依赖 - 📦内置包索引:通过
make search q=关键词可搜索数百个常见 Erlang 项目 - 🔄自动转换(Autopatch):Rebar 项目、纯 Erlang 项目都能被自动"改造"为 erlang.mk 项目,无需手工处理
- ⚡构建缓存:编译过的依赖不会重复编译,可选开启 Git/Hex 依赖缓存,CI 场景提速明显
二、快速开始:安装与第一个依赖
如果需要在本地获取 erlang.mk 源码(克隆到项目根目录即可):
git clone https://gitcode.com/gh_mirrors/er/erlang.mk在项目根目录创建Makefile,内容只需:
PROJECT = my_app ERL_OPTS = -I include DEPS = cowboy include erlang.mk执行make后,Cowboy 会被自动下载到deps/目录并编译完成,之后make shell、make test等操作都自动包含该依赖,无需任何额外配置。
💡 提示:包索引并非总是最新版本,建议显式指定版本,后文会讲。
三、认识 DEPS 家族:7 种依赖变量一次讲清
Erlang.mk 用不同的 Makefile 变量区分依赖的用途,这是依赖管理中最关键的一块:
| 变量 | 用途 | 示例 |
|---|---|---|
DEPS | 运行时依赖,会写入 app.src | DEPS = cowboy |
BUILD_DEPS | 仅构建时使用(如编译期工具、NIF 库) | BUILD_DEPS = erlando |
LOCAL_DEPS | 本地 OTP 应用或仓库内应用,不下载 | LOCAL_DEPS = crypto |
TEST_DEPS | 仅测试时使用 | TEST_DEPS = ct_helper |
DOC_DEPS | 仅构建文档时使用 | DOC_DEPS = edown |
REL_DEPS | 打包 release 时需要 | REL_DEPS = recon |
SHELL_DEPS | 仅make shell时可用 | SHELL_DEPS = tddreloader |
此外还有两个实用变量:
IGNORE_DEPS:彻底忽略某些依赖(不下载、不编译),例如系统已安装的应用OPTIONAL_DEPS:声明可选依赖,由顶层项目决定是否纳入
依赖的默认存放位置由DEPS_DIR控制(默认为deps/)。如需修改,请使用DEPS_DIR ?= $(CURDIR)/libs这种弱赋值写法,并保留$(CURDIR)前缀,否则二级依赖会下载到错误目录。
四、git 依赖实战:拉取方式与版本锁定
4.1 从包索引自动拉取(默认)
只写DEPS = cowboy时,erlang.mk 会先查内置包索引,自动决定仓库地址和版本。索引覆盖 Cowboy、Ranch、Gun、Cowlib 等常用项目(对应源码见index/目录,如index/cowboy.mk、index/relx.mk)。
4.2 显式指定 git 仓库
需要用自己的 fork、或项目不在索引中时,用dep_依赖名变量完整指定"拉取方式 + 仓库 + 版本":
DEPS = cowboy dep_cowboy = git https://github.com/essen/cowboy 2.12.0其中第三个参数可以是任意tag、分支名或 commit,例如master、v2.0.0或完整哈希。
4.3 只锁定版本
如果只想改版本号,不想动仓库地址:
DEPS = cowboy dep_cowboy_commit = 2.12.04.4 其他拉取方式速查
erlang.mk 支持的拉取方法(fetch method)相当齐全,全部实现在 core/deps.mk 中:
| 方式 | 格式 | 说明 |
|---|---|---|
git | git 仓库 版本 | 克隆并 checkout 指定版本 |
git-subfolder | git 仓库 版本 子目录 | 只取大仓库中的某个子目录 |
git-submodule | git-submodule | 初始化 git 子模块(需事先git submodule add) |
hg | hg 仓库 版本 | 克隆 Mercurial 仓库 |
svn | svn 仓库地址 | 检出 SVN 仓库 |
cp | cp 本地路径 | 复制本机目录 |
ln | ln 本地路径 | 符号链接本机目录(链接的依赖每次都会重新编译) |
hex | hex 版本 [包名] | 从 Hex 仓库下载(详见下节) |
还支持自定义拉取方式:定义任何名为dep_fetch_方式名的变量即可,只要最终在deps/依赖名/下生成目录内容。
五、hex 依赖:接入 Hex 包仓库
对于发布在 Hex 仓库上的包(例如 Hex 包名与 Erlang 应用名不一致的场景),erlang.mk 提供了hex拉取方式:
DEPS = uuid dep_uuid = hex 1.7.5 uuid_erl含义是:应用名为uuid,从 Hex 下载版本1.7.5,实际包名为uuid_erl(只有包名与应用名不同才需要第三个参数)。
值得了解的一个细节:当你的项目使用 hex 依赖时,erlang.mk 会自动引入并编译hex_core作为底层下载器,整个过程无需你手动配置。
六、缓存机制:让构建更快一步
erlang.mk 内置了两层缓存,分别解决不同场景的痛点。
6.1 编译结果缓存(默认开启)
依赖一旦编译成功,ebin/下会生成dep_built标记文件,之后执行make会自动跳过已编译的依赖——这是 erlang.mk 构建快的核心原因。想强制重新编译某个依赖,有四种方式:
- 该依赖目录是符号链接(
ln方式天然每次都重编译) - 直接进入依赖目录执行
make - 使用
make FULL=1强制重编译全部依赖 - 删除依赖下的
ebin/dep_built文件 - (进阶)
make FORCE_REBUILD=依赖名只重编译指定依赖
6.2 Git/Hex 依赖下载缓存(CACHE_DEPS)
开启方式一行搞定:
CACHE_DEPS = 1开启后(实现见 core/deps.mk):
- git 依赖:完整仓库先克隆到缓存目录(默认
~/.cache/erlang.mk/git/,可用XDG_CACHE_HOME或CACHE_DIR变量修改),deps/下只做一个轻量克隆;再次拉取同一仓库的其他版本时直接走缓存,速度大幅提升 - hex 依赖:tarball 压缩包缓存在
~/.cache/erlang.mk/hex/,同一版本的包只下载一次
清理缓存用make cacheclean(会同时清掉 git 与 hex 缓存)。这一特性对多版本切换、CI 流水线尤其有价值。
6.3 Beam 编译缓存(beam-cache)
较新版本引入的 beam-cache(实现在 core/beam-cache.mk)解决另一个问题:在"普通构建"和"测试构建"之间来回切换时,Erlang.mk 会把ebin/的编译产物暂存到.erlang.mk/beam-cache/下,切换时直接换回,避免重复编译,进一步缩短开发-测试循环时间。
七、依赖构建顺序与版本冲突规则
依赖按先序遍历规则处理:先拉取当前应用的全部依赖,再依次递归构建。版本冲突的解决规则非常明确——先被拉取到的版本获胜:
- 项目 A 依赖 B、C,而 B、C 都依赖不同版本的 D → B 要求的 D 版本获胜(因为先处理 B)
- A、B、C 都依赖不同版本的 D → A 指定的版本永远获胜
理解这一点,就能解释"为什么我的依赖版本和我预期的不一样"这类问题。
只拉取不编译、排查依赖树也很方便:
make fetch-deps # 递归下载所有依赖(不编译) make list-deps # 递归列出所有依赖路径 make query-deps # 输出依赖名、拉取方式、仓库、版本 make list-test-deps # 列出测试依赖 make query-deps QUERY="name fetch_method repo version extra absolute_path"这些命令由 core/deps-tools.mk 提供,query-deps的输出字段可通过QUERY变量自定义。
八、单仓库多应用(Monorepo)与 Autopatch
8.1 apps/ 多应用目录
如果你在一个仓库里维护多个应用,只需创建apps/目录(APPS_DIR可自定义),每个子目录放一个应用,并在各自 Makefile 中用LOCAL_DEPS声明本地依赖。Erlang.mk 会自动按正确顺序编译所有应用;本地依赖与远程依赖重名时,本地版本优先。
还可以用make new-app in=xxx/make new-lib in=xxx快速生成应用骨架(模板位于templates/目录)。
8.2 Autopatch 自动适配
拉取依赖后 erlang.mk 会自动执行 Autopatch:Rebar 项目会被自动转换、erlang.mk 项目的 Makefile 会被统一指向顶层项目的 erlang.mk、没有 Makefile 的纯 Erlang 项目会自动生成构建脚本。不想让它动某些项目时,用NO_AUTOPATCH = cowboy ranch排除;也可以扩展autopatch-依赖名::目标,在拉取后打补丁或追加编译选项。
九、离线场景与常用排错
- 无网络环境:
make SKIP_DEPS=1可跳过所有依赖下载与编译 - 重复模块报错:在
deps::目标里加一行命令删除冲突的.erl文件即可 - 升级 erlang.mk 后行为异常:执行
make distclean或手动删除.erlang.mk临时目录后重试
十、关键文件索引(延伸阅读)
- 依赖管理核心实现:core/deps.mk
- 依赖查询/列表命令:core/deps-tools.mk
- Beam 编译缓存:core/beam-cache.mk
- 包索引(Cowboy/Ranch/Gun 等):index/
- 官方依赖章节文档:doc/src/guide/deps.asciidoc
- 依赖相关测试用例:test/core_deps.mk
总结
erlang.mk 的依赖管理可以浓缩为一句话:用DEPS及同族变量声明、用dep_xxx精确控制来源与版本、用CACHE_DEPS加速重复拉取。掌握本文的 7 种依赖变量、9 种拉取方式和两层缓存机制后,你就能从容应对绝大多数 Erlang 项目的依赖场景——这正是 "just works" 的含义所在。🚀
【免费下载链接】erlang.mkA build tool for Erlang that just works.项目地址: https://gitcode.com/gh_mirrors/er/erlang.mk
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考